image.go 4.1 KB

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