123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- package base64Captcha
- import (
- "bytes"
- "encoding/base64"
- "errors"
- "fmt"
- "github.com/golang/freetype"
- "github.com/golang/freetype/truetype"
- "golang.org/x/image/font"
- "image"
- "image/color"
- "image/draw"
- "image/png"
- "io"
- "log"
- "math"
- "math/rand"
- )
- //ItemChar captcha item of unicode characters
- type ItemChar struct {
- bgColor color.Color
- width int
- height int
- nrgba *image.NRGBA
- }
- //NewItemChar creates a captcha item of characters
- func NewItemChar(w int, h int, bgColor color.RGBA) *ItemChar {
- d := ItemChar{width: w, height: h}
- m := image.NewNRGBA(image.Rect(0, 0, w, h))
- draw.Draw(m, m.Bounds(), &image.Uniform{bgColor}, image.ZP, draw.Src)
- d.nrgba = m
- return &d
- }
- //drawHollowLine draw strong and bold white line.
- func (item *ItemChar) drawHollowLine() *ItemChar {
- first := item.width / 20
- end := first * 19
- lineColor := RandLightColor()
- x1 := float64(rand.Intn(first))
- //y1 := float64(rand.Intn(y)+y);
- x2 := float64(rand.Intn(first) + end)
- multiple := float64(rand.Intn(5)+3) / float64(5)
- if int(multiple*10)%3 == 0 {
- multiple = multiple * -1.0
- }
- w := item.height / 20
- for ; x1 < x2; x1++ {
- y := math.Sin(x1*math.Pi*multiple/float64(item.width)) * float64(item.height/3)
- if multiple < 0 {
- y = y + float64(item.height/2)
- }
- item.nrgba.Set(int(x1), int(y), lineColor)
- for i := 0; i <= w; i++ {
- item.nrgba.Set(int(x1), int(y)+i, lineColor)
- }
- }
- return item
- }
- //drawSineLine draw a sine line.
- func (item *ItemChar) drawSineLine() *ItemChar {
- var py float64
- //振幅
- a := rand.Intn(item.height / 2)
- //Y轴方向偏移量
- b := random(int64(-item.height/4), int64(item.height/4))
- //X轴方向偏移量
- f := random(int64(-item.height/4), int64(item.height/4))
- // 周期
- var t float64
- if item.height > item.width/2 {
- t = random(int64(item.width/2), int64(item.height))
- } else if item.height == item.width/2 {
- t = float64(item.height)
- } else {
- t = random(int64(item.height), int64(item.width/2))
- }
- w := float64((2 * math.Pi) / t)
- // 曲线横坐标起始位置
- px1 := 0
- px2 := int(random(int64(float64(item.width)*0.8), int64(item.width)))
- c := RandDeepColor()
- for px := px1; px < px2; px++ {
- if w != 0 {
- py = float64(a)*math.Sin(w*float64(px)+f) + b + (float64(item.width) / float64(5))
- i := item.height / 5
- for i > 0 {
- item.nrgba.Set(px+i, int(py), c)
- //fmt.Println(px + i,int(py) )
- i--
- }
- }
- }
- return item
- }
- //drawSlimLine draw n slim-random-color lines.
- func (item *ItemChar) drawSlimLine(num int) *ItemChar {
- first := item.width / 10
- end := first * 9
- y := item.height / 3
- for i := 0; i < num; i++ {
- point1 := point{X: rand.Intn(first), Y: rand.Intn(y)}
- point2 := point{X: rand.Intn(first) + end, Y: rand.Intn(y)}
- if i%2 == 0 {
- point1.Y = rand.Intn(y) + y*2
- point2.Y = rand.Intn(y)
- } else {
- point1.Y = rand.Intn(y) + y*(i%2)
- point2.Y = rand.Intn(y) + y*2
- }
- item.drawBeeline(point1, point2, RandDeepColor())
- }
- return item
- }
- func (item *ItemChar) drawBeeline(point1 point, point2 point, lineColor color.RGBA) {
- dx := math.Abs(float64(point1.X - point2.X))
- dy := math.Abs(float64(point2.Y - point1.Y))
- sx, sy := 1, 1
- if point1.X >= point2.X {
- sx = -1
- }
- if point1.Y >= point2.Y {
- sy = -1
- }
- err := dx - dy
- for {
- item.nrgba.Set(point1.X, point1.Y, lineColor)
- item.nrgba.Set(point1.X+1, point1.Y, lineColor)
- item.nrgba.Set(point1.X-1, point1.Y, lineColor)
- item.nrgba.Set(point1.X+2, point1.Y, lineColor)
- item.nrgba.Set(point1.X-2, point1.Y, lineColor)
- if point1.X == point2.X && point1.Y == point2.Y {
- return
- }
- e2 := err * 2
- if e2 > -dy {
- err -= dy
- point1.X += sx
- }
- if e2 < dx {
- err += dx
- point1.Y += sy
- }
- }
- }
- func (item *ItemChar) drawNoise(noiseText string, fonts []*truetype.Font) error {
- c := freetype.NewContext()
- c.SetDPI(imageStringDpi)
- c.SetClip(item.nrgba.Bounds())
- c.SetDst(item.nrgba)
- c.SetHinting(font.HintingFull)
- rawFontSize := float64(item.height) / (1 + float64(rand.Intn(7))/float64(10))
- for _, char := range noiseText {
- rw := rand.Intn(item.width)
- rh := rand.Intn(item.height)
- fontSize := rawFontSize/2 + float64(rand.Intn(5))
- c.SetSrc(image.NewUniform(RandLightColor()))
- c.SetFontSize(fontSize)
- c.SetFont(randFontFrom(fonts))
- pt := freetype.Pt(rw, rh)
- if _, err := c.DrawString(string(char), pt); err != nil {
- log.Println(err)
- }
- }
- return nil
- }
- //drawText draw captcha string to image.把文字写入图像验证码
- func (item *ItemChar) drawText(text string, fonts []*truetype.Font) error {
- c := freetype.NewContext()
- c.SetDPI(imageStringDpi)
- c.SetClip(item.nrgba.Bounds())
- c.SetDst(item.nrgba)
- c.SetHinting(font.HintingFull)
- if len(text) == 0 {
- return errors.New("text must not be empty, there is nothing to draw")
- }
- fontWidth := item.width / len(text)
- for i, s := range text {
- fontSize := item.height * (rand.Intn(7) + 7) / 16
- c.SetSrc(image.NewUniform(RandDeepColor()))
- c.SetFontSize(float64(fontSize))
- c.SetFont(randFontFrom(fonts))
- x := fontWidth*i + fontWidth/fontSize
- y := item.height/2 + fontSize/2 - rand.Intn(item.height/16*3)
- pt := freetype.Pt(x, y)
- if _, err := c.DrawString(string(s), pt); err != nil {
- return err
- }
- }
- return nil
- }
- //BinaryEncoding encodes an image to PNG and returns a byte slice.
- func (item *ItemChar) BinaryEncoding() []byte {
- var buf bytes.Buffer
- if err := png.Encode(&buf, item.nrgba); err != nil {
- panic(err.Error())
- }
- return buf.Bytes()
- }
- // WriteTo writes captcha character in png format into the given io.Writer, and
- // returns the number of bytes written and an error if any.
- func (item *ItemChar) WriteTo(w io.Writer) (int64, error) {
- n, err := w.Write(item.BinaryEncoding())
- return int64(n), err
- }
- // EncodeB64string encodes an image to base64 string
- func (item *ItemChar) EncodeB64string() string {
- return fmt.Sprintf("data:%s;base64,%s", MimeTypeImage, base64.StdEncoding.EncodeToString(item.BinaryEncoding()))
- }
- type point struct {
- X int
- Y int
- }
|