Browse Source

image: new wave distortion, and strike-through.

Dmitry Chestnykh 14 years ago
parent
commit
1b34f643a5
1 changed files with 54 additions and 21 deletions
  1. 54 21
      image.go

+ 54 - 21
image.go

@@ -4,6 +4,7 @@ import (
 	"image"
 	"image"
 	"image/png"
 	"image/png"
 	"io"
 	"io"
+	"math"
 	"os"
 	"os"
 	"rand"
 	"rand"
 	"time"
 	"time"
@@ -11,7 +12,7 @@ import (
 
 
 const (
 const (
 	// Standard width and height of a captcha image.
 	// Standard width and height of a captcha image.
-	StdWidth  = 300
+	StdWidth  = 240
 	StdHeight = 80
 	StdHeight = 80
 
 
 	maxSkew = 0.7
 	maxSkew = 0.7
@@ -41,13 +42,17 @@ func NewImage(digits []byte, width, height int) *Image {
 		0xFF,
 		0xFF,
 	}
 	}
 	img.calculateSizes(width, height, len(digits))
 	img.calculateSizes(width, height, len(digits))
-	// Draw background (10 random circles of random brightness).
-	img.fillWithCircles(10, img.dotSize)
 	// Randomly position captcha inside the image.
 	// Randomly position captcha inside the image.
 	maxx := width - (img.numWidth+img.dotSize)*len(digits) - img.dotSize
 	maxx := width - (img.numWidth+img.dotSize)*len(digits) - img.dotSize
 	maxy := height - img.numHeight - img.dotSize*2
 	maxy := height - img.numHeight - img.dotSize*2
-	x := rnd(img.dotSize*2, maxx)
-	y := rnd(img.dotSize*2, maxy)
+	var border int
+	if width > height {
+		border = height / 5
+	} else {
+		border = width / 5
+	}
+	x := rnd(border, maxx-border)
+	y := rnd(border, maxy-border)
 	// Draw digits.
 	// Draw digits.
 	for _, n := range digits {
 	for _, n := range digits {
 		img.drawDigit(font[n], x, y)
 		img.drawDigit(font[n], x, y)
@@ -55,6 +60,10 @@ func NewImage(digits []byte, width, height int) *Image {
 	}
 	}
 	// Draw strike-through line.
 	// Draw strike-through line.
 	img.strikeThrough()
 	img.strikeThrough()
+	// Apply wave distortion.
+	img.distort(rndf(5, 10), rndf(100, 200))
+	// Draw background (20 random circles of random brightness).
+	img.fillWithCircles(20, img.dotSize)
 	return img
 	return img
 }
 }
 
 
@@ -71,9 +80,9 @@ func (img *Image) calculateSizes(width, height, ncount int) {
 	// Goal: fit all digits inside the image.
 	// Goal: fit all digits inside the image.
 	var border int
 	var border int
 	if width > height {
 	if width > height {
-		border = height / 5
+		border = height / 4
 	} else {
 	} else {
-		border = width / 5
+		border = width / 4
 	}
 	}
 	// Convert everything to floats for calculations.
 	// Convert everything to floats for calculations.
 	w := float64(width - border*2)
 	w := float64(width - border*2)
@@ -146,43 +155,67 @@ func (img *Image) fillWithCircles(n, maxradius int) {
 }
 }
 
 
 func (img *Image) strikeThrough() {
 func (img *Image) strikeThrough() {
-	r := 0
+	wave := rndf(5, 20)
+	height := img.dotSize
 	maxx := img.Bounds().Max.X
 	maxx := img.Bounds().Max.X
 	maxy := img.Bounds().Max.Y
 	maxy := img.Bounds().Max.Y
 	y := rnd(maxy/3, maxy-maxy/3)
 	y := rnd(maxy/3, maxy-maxy/3)
-	for x := 0; x < maxx; x += r {
-		r = rnd(1, img.dotSize/3)
-		y += rnd(-img.dotSize/2, img.dotSize/2)
-		if y <= 0 || y >= maxy {
-			y = rnd(maxy/3, maxy-maxy/3)
+	freq := rndf(80, 180)
+	for x := 0; x < maxx; x++ {
+		xo := wave * math.Cos(2.0*math.Pi*float64(y)/freq)
+		yo := wave * math.Sin(2.0*math.Pi*float64(x)/freq)
+		for yn := 0; yn < height; yn++ {
+			r := rnd(0, img.dotSize)
+			img.drawCircle(img.primaryColor, x+int(xo), y+int(yo)+(yn*img.dotSize), r/2)
 		}
 		}
-		img.drawCircle(img.primaryColor, x, y, r)
 	}
 	}
 }
 }
 
 
 func (img *Image) drawDigit(digit []byte, x, y int) {
 func (img *Image) drawDigit(digit []byte, x, y int) {
 	skf := rndf(-maxSkew, maxSkew)
 	skf := rndf(-maxSkew, maxSkew)
 	xs := float64(x)
 	xs := float64(x)
-	minr := img.dotSize / 2 // minumum radius
-	maxr := img.dotSize     // maximum radius
-	y += rnd(-minr, minr)
+	r := img.dotSize/2
+	y += rnd(-r, r)
 	for yy := 0; yy < fontHeight; yy++ {
 	for yy := 0; yy < fontHeight; yy++ {
 		for xx := 0; xx < fontWidth; xx++ {
 		for xx := 0; xx < fontWidth; xx++ {
 			if digit[yy*fontWidth+xx] != blackChar {
 			if digit[yy*fontWidth+xx] != blackChar {
 				continue
 				continue
 			}
 			}
 			// Introduce random variations.
 			// Introduce random variations.
-			or := rnd(minr, maxr)
-			ox := x + (xx * img.dotSize) + rnd(0, or/2)
-			oy := y + (yy * img.dotSize) + rnd(0, or/2)
+			ox := x + (xx * img.dotSize) + rnd(0, r/2)
+			oy := y + (yy * img.dotSize) + rnd(0, r/2)
 
 
-			img.drawCircle(img.primaryColor, ox, oy, or)
+			img.drawCircle(img.primaryColor, ox, oy, r)
 		}
 		}
 		xs += skf
 		xs += skf
 		x = int(xs)
 		x = int(xs)
 	}
 	}
 }
 }
 
 
+func fmin(a, b float64) float64 {
+	if a < b {
+		return a
+	}
+	return b
+}
+
+func (img *Image) distort(wave float64, freq float64) {
+	nWidth := img.Bounds().Max.X
+	nHeight := img.Bounds().Max.Y
+
+	oldimg := img.NRGBA
+	newimg := image.NewNRGBA(nWidth, nHeight)
+
+	for x := 0; x < nWidth; x++ {
+		for y := 0; y < nHeight; y++ {
+			ox := wave * math.Sin(2.0*math.Pi*float64(y)/freq)
+			oy := wave * math.Cos(2.0*math.Pi*float64(x)/freq)
+			newimg.Set(x, y, oldimg.At(x + int(ox), y + int(oy)))
+		}
+	}
+	img.NRGBA = newimg
+}
+
 func setRandomBrightness(c *image.NRGBAColor, max uint8) {
 func setRandomBrightness(c *image.NRGBAColor, max uint8) {
 	minc := min3(c.R, c.G, c.B)
 	minc := min3(c.R, c.G, c.B)
 	maxc := max3(c.R, c.G, c.B)
 	maxc := max3(c.R, c.G, c.B)