item_char.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. package base64Captcha
  2. import (
  3. "bytes"
  4. "encoding/base64"
  5. "errors"
  6. "fmt"
  7. "github.com/golang/freetype"
  8. "github.com/golang/freetype/truetype"
  9. "golang.org/x/image/font"
  10. "image"
  11. "image/color"
  12. "image/draw"
  13. "image/png"
  14. "io"
  15. "log"
  16. "math"
  17. "math/rand"
  18. )
  19. //ItemChar captcha item of unicode characters
  20. type ItemChar struct {
  21. bgColor color.Color
  22. width int
  23. height int
  24. nrgba *image.NRGBA
  25. }
  26. //NewItemChar creates a captcha item of characters
  27. func NewItemChar(w int, h int, bgColor color.RGBA) *ItemChar {
  28. d := ItemChar{width: w, height: h}
  29. m := image.NewNRGBA(image.Rect(0, 0, w, h))
  30. draw.Draw(m, m.Bounds(), &image.Uniform{bgColor}, image.ZP, draw.Src)
  31. d.nrgba = m
  32. return &d
  33. }
  34. //drawHollowLine draw strong and bold white line.
  35. func (item *ItemChar) drawHollowLine() *ItemChar {
  36. first := item.width / 20
  37. end := first * 19
  38. lineColor := RandLightColor()
  39. x1 := float64(rand.Intn(first))
  40. //y1 := float64(rand.Intn(y)+y);
  41. x2 := float64(rand.Intn(first) + end)
  42. multiple := float64(rand.Intn(5)+3) / float64(5)
  43. if int(multiple*10)%3 == 0 {
  44. multiple = multiple * -1.0
  45. }
  46. w := item.height / 20
  47. for ; x1 < x2; x1++ {
  48. y := math.Sin(x1*math.Pi*multiple/float64(item.width)) * float64(item.height/3)
  49. if multiple < 0 {
  50. y = y + float64(item.height/2)
  51. }
  52. item.nrgba.Set(int(x1), int(y), lineColor)
  53. for i := 0; i <= w; i++ {
  54. item.nrgba.Set(int(x1), int(y)+i, lineColor)
  55. }
  56. }
  57. return item
  58. }
  59. //drawSineLine draw a sine line.
  60. func (item *ItemChar) drawSineLine() *ItemChar {
  61. var py float64
  62. //振幅
  63. a := rand.Intn(item.height / 2)
  64. //Y轴方向偏移量
  65. b := random(int64(-item.height/4), int64(item.height/4))
  66. //X轴方向偏移量
  67. f := random(int64(-item.height/4), int64(item.height/4))
  68. // 周期
  69. var t float64
  70. if item.height > item.width/2 {
  71. t = random(int64(item.width/2), int64(item.height))
  72. } else if item.height == item.width/2 {
  73. t = float64(item.height)
  74. } else {
  75. t = random(int64(item.height), int64(item.width/2))
  76. }
  77. w := float64((2 * math.Pi) / t)
  78. // 曲线横坐标起始位置
  79. px1 := 0
  80. px2 := int(random(int64(float64(item.width)*0.8), int64(item.width)))
  81. c := RandDeepColor()
  82. for px := px1; px < px2; px++ {
  83. if w != 0 {
  84. py = float64(a)*math.Sin(w*float64(px)+f) + b + (float64(item.width) / float64(5))
  85. i := item.height / 5
  86. for i > 0 {
  87. item.nrgba.Set(px+i, int(py), c)
  88. //fmt.Println(px + i,int(py) )
  89. i--
  90. }
  91. }
  92. }
  93. return item
  94. }
  95. //drawSlimLine draw n slim-random-color lines.
  96. func (item *ItemChar) drawSlimLine(num int) *ItemChar {
  97. first := item.width / 10
  98. end := first * 9
  99. y := item.height / 3
  100. for i := 0; i < num; i++ {
  101. point1 := point{X: rand.Intn(first), Y: rand.Intn(y)}
  102. point2 := point{X: rand.Intn(first) + end, Y: rand.Intn(y)}
  103. if i%2 == 0 {
  104. point1.Y = rand.Intn(y) + y*2
  105. point2.Y = rand.Intn(y)
  106. } else {
  107. point1.Y = rand.Intn(y) + y*(i%2)
  108. point2.Y = rand.Intn(y) + y*2
  109. }
  110. item.drawBeeline(point1, point2, RandDeepColor())
  111. }
  112. return item
  113. }
  114. func (item *ItemChar) drawBeeline(point1 point, point2 point, lineColor color.RGBA) {
  115. dx := math.Abs(float64(point1.X - point2.X))
  116. dy := math.Abs(float64(point2.Y - point1.Y))
  117. sx, sy := 1, 1
  118. if point1.X >= point2.X {
  119. sx = -1
  120. }
  121. if point1.Y >= point2.Y {
  122. sy = -1
  123. }
  124. err := dx - dy
  125. for {
  126. item.nrgba.Set(point1.X, point1.Y, lineColor)
  127. item.nrgba.Set(point1.X+1, point1.Y, lineColor)
  128. item.nrgba.Set(point1.X-1, point1.Y, lineColor)
  129. item.nrgba.Set(point1.X+2, point1.Y, lineColor)
  130. item.nrgba.Set(point1.X-2, point1.Y, lineColor)
  131. if point1.X == point2.X && point1.Y == point2.Y {
  132. return
  133. }
  134. e2 := err * 2
  135. if e2 > -dy {
  136. err -= dy
  137. point1.X += sx
  138. }
  139. if e2 < dx {
  140. err += dx
  141. point1.Y += sy
  142. }
  143. }
  144. }
  145. func (item *ItemChar) drawNoise(noiseText string, fonts []*truetype.Font) error {
  146. c := freetype.NewContext()
  147. c.SetDPI(imageStringDpi)
  148. c.SetClip(item.nrgba.Bounds())
  149. c.SetDst(item.nrgba)
  150. c.SetHinting(font.HintingFull)
  151. rawFontSize := float64(item.height) / (1 + float64(rand.Intn(7))/float64(10))
  152. for _, char := range noiseText {
  153. rw := rand.Intn(item.width)
  154. rh := rand.Intn(item.height)
  155. fontSize := rawFontSize/2 + float64(rand.Intn(5))
  156. c.SetSrc(image.NewUniform(RandLightColor()))
  157. c.SetFontSize(fontSize)
  158. c.SetFont(randFontFrom(fonts))
  159. pt := freetype.Pt(rw, rh)
  160. if _, err := c.DrawString(string(char), pt); err != nil {
  161. log.Println(err)
  162. }
  163. }
  164. return nil
  165. }
  166. //drawText draw captcha string to image.把文字写入图像验证码
  167. func (item *ItemChar) drawText(text string, fonts []*truetype.Font) error {
  168. c := freetype.NewContext()
  169. c.SetDPI(imageStringDpi)
  170. c.SetClip(item.nrgba.Bounds())
  171. c.SetDst(item.nrgba)
  172. c.SetHinting(font.HintingFull)
  173. if len(text) == 0 {
  174. return errors.New("text must not be empty, there is nothing to draw")
  175. }
  176. fontWidth := item.width / len(text)
  177. for i, s := range text {
  178. fontSize := item.height * (rand.Intn(7) + 7) / 16
  179. c.SetSrc(image.NewUniform(RandDeepColor()))
  180. c.SetFontSize(float64(fontSize))
  181. c.SetFont(randFontFrom(fonts))
  182. x := fontWidth*i + fontWidth/fontSize
  183. y := item.height/2 + fontSize/2 - rand.Intn(item.height/16*3)
  184. pt := freetype.Pt(x, y)
  185. if _, err := c.DrawString(string(s), pt); err != nil {
  186. return err
  187. }
  188. }
  189. return nil
  190. }
  191. //BinaryEncoding encodes an image to PNG and returns a byte slice.
  192. func (item *ItemChar) BinaryEncoding() []byte {
  193. var buf bytes.Buffer
  194. if err := png.Encode(&buf, item.nrgba); err != nil {
  195. panic(err.Error())
  196. }
  197. return buf.Bytes()
  198. }
  199. // WriteTo writes captcha character in png format into the given io.Writer, and
  200. // returns the number of bytes written and an error if any.
  201. func (item *ItemChar) WriteTo(w io.Writer) (int64, error) {
  202. n, err := w.Write(item.BinaryEncoding())
  203. return int64(n), err
  204. }
  205. // EncodeB64string encodes an image to base64 string
  206. func (item *ItemChar) EncodeB64string() string {
  207. return fmt.Sprintf("data:%s;base64,%s", MimeTypeImage, base64.StdEncoding.EncodeToString(item.BinaryEncoding()))
  208. }
  209. type point struct {
  210. X int
  211. Y int
  212. }