加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
captcha.go 9.97 KB
一键复制 编辑 原始数据 按行查看 历史
Minho 提交于 2018-02-05 16:22 . 格式化代码
package gocaptcha
import (
"errors"
"flag"
"image"
"image/color"
"image/draw"
"image/gif"
"image/jpeg"
"image/png"
"io"
"io/ioutil"
"log"
"math"
"math/rand"
"os"
"strings"
"time"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
)
var (
dpi = flag.Float64("dpi", 72, "screen resolution in Dots Per Inch")
r = rand.New(rand.NewSource(time.Now().UnixNano()))
FontFamily []string = make([]string, 0)
)
const txtChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
const (
//图片格式
ImageFormatPng = iota
ImageFormatJpeg
ImageFormatGif
//验证码噪点强度
CaptchaComplexLower = iota
CaptchaComplexMedium
CaptchaComplexHigh
)
type CaptchaImage struct {
nrgba *image.NRGBA
width int
height int
Complex int
}
//获取指定目录下的所有文件,不进入下一级目录搜索,可以匹配后缀过滤。
func ReadFonts(dirPth string, suffix string) (err error) {
files := make([]string, 0, 10)
dir, err := ioutil.ReadDir(dirPth)
if err != nil {
return err
}
PthSep := string(os.PathSeparator)
suffix = strings.ToUpper(suffix) //忽略后缀匹配的大小写
for _, fi := range dir {
if fi.IsDir() { // 忽略目录
continue
}
if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) { //匹配文件
files = append(files, dirPth+PthSep+fi.Name())
}
}
SetFontFamily(files...)
return nil
}
//新建一个图片对象
func NewCaptchaImage(width int, height int, bgColor color.RGBA) (*CaptchaImage, error) {
m := image.NewNRGBA(image.Rect(0, 0, width, height))
draw.Draw(m, m.Bounds(), &image.Uniform{bgColor}, image.ZP, draw.Src)
return &CaptchaImage{
nrgba: m,
height: height,
width: width,
}, nil
}
//保存图片对象
func (this *CaptchaImage) SaveImage(w io.Writer, imageFormat int) error {
if imageFormat == ImageFormatPng {
return png.Encode(w, this.nrgba)
}
if imageFormat == ImageFormatJpeg {
return jpeg.Encode(w, this.nrgba, &jpeg.Options{100})
}
if imageFormat == ImageFormatGif {
return gif.Encode(w, this.nrgba, &gif.Options{NumColors: 256})
}
return errors.New("Not supported image format.")
}
//添加一个较粗的空白直线
func (captcha *CaptchaImage) DrawHollowLine() *CaptchaImage {
first := (captcha.width / 20)
end := first * 19
lineColor := color.RGBA{R: 245, G: 250, B: 251, A: 255}
x1 := float64(r.Intn(first))
//y1 := float64(r.Intn(y)+y);
x2 := float64(r.Intn(first) + end)
multiple := float64(r.Intn(5)+3) / float64(5)
if int(multiple*10)%3 == 0 {
multiple = multiple * -1.0
}
w := captcha.height / 20
for ; x1 < x2; x1++ {
y := math.Sin(x1*math.Pi*multiple/float64(captcha.width)) * float64(captcha.height/3)
if multiple < 0 {
y = y + float64(captcha.height/2)
}
captcha.nrgba.Set(int(x1), int(y), lineColor)
for i := 0; i <= w; i++ {
captcha.nrgba.Set(int(x1), int(y)+i, lineColor)
}
}
return captcha
}
func (captcha *CaptchaImage) DrawSineLine() *CaptchaImage {
px := 0
var py float64 = 0
//振幅
a := r.Intn(captcha.height / 2)
//Y轴方向偏移量
b := Random(int64(-captcha.height/4), int64(captcha.height/4))
//X轴方向偏移量
f := Random(int64(-captcha.height/4), int64(captcha.height/4))
// 周期
var t float64 = 0
if captcha.height > captcha.width/2 {
t = Random(int64(captcha.width/2), int64(captcha.height))
} else {
t = Random(int64(captcha.height), int64(captcha.width/2))
}
w := float64((2 * math.Pi) / t)
// 曲线横坐标起始位置
px1 := 0
px2 := int(Random(int64(float64(captcha.width)*0.8), int64(captcha.width)))
c := color.RGBA{R: uint8(r.Intn(150)), G: uint8(r.Intn(150)), B: uint8(r.Intn(150)), A: uint8(255)}
for px = px1; px < px2; px++ {
if w != 0 {
py = float64(a)*math.Sin(w*float64(px)+f) + b + (float64(captcha.width) / float64(5))
i := captcha.height / 5
for i > 0 {
captcha.nrgba.Set(px+i, int(py), c)
//fmt.Println(px + i,int(py) )
i--
}
}
}
return captcha
}
//画一条直线
func (captcha *CaptchaImage) Drawline(num int) *CaptchaImage {
first := (captcha.width / 10)
end := first * 9
y := captcha.height / 3
for i := 0; i < num; i++ {
point1 := Point{X: r.Intn(first), Y: r.Intn(y)}
point2 := Point{X: r.Intn(first) + end, Y: r.Intn(y)}
if i%2 == 0 {
point1.Y = r.Intn(y) + y*2
point2.Y = r.Intn(y)
} else {
point1.Y = r.Intn(y) + y*(i%2)
point2.Y = r.Intn(y) + y*2
}
captcha.drawBeeline(point1, point2, RandDeepColor())
}
return captcha
}
func (captcha *CaptchaImage) 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 {
captcha.nrgba.Set(point1.X, point1.Y, lineColor)
captcha.nrgba.Set(point1.X+1, point1.Y, lineColor)
captcha.nrgba.Set(point1.X-1, point1.Y, lineColor)
captcha.nrgba.Set(point1.X+2, point1.Y, lineColor)
captcha.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 (captcha *CaptchaImage) DrawBorder(borderColor color.RGBA) *CaptchaImage {
for x := 0; x < captcha.width; x++ {
captcha.nrgba.Set(x, 0, borderColor)
captcha.nrgba.Set(x, captcha.height-1, borderColor)
}
for y := 0; y < captcha.height; y++ {
captcha.nrgba.Set(0, y, borderColor)
captcha.nrgba.Set(captcha.width-1, y, borderColor)
}
return captcha
}
//画噪点.
func (captcha *CaptchaImage) DrawNoise(complex int) *CaptchaImage {
density := 18
if complex == CaptchaComplexLower {
density = 28
} else if complex == CaptchaComplexMedium {
density = 18
} else if complex == CaptchaComplexHigh {
density = 8
}
maxSize := (captcha.height * captcha.width) / density
for i := 0; i < maxSize; i++ {
rw := r.Intn(captcha.width)
rh := r.Intn(captcha.height)
captcha.nrgba.Set(rw, rh, RandColor())
size := r.Intn(maxSize)
if size%3 == 0 {
captcha.nrgba.Set(rw+1, rh+1, RandColor())
}
}
return captcha
}
//画文字噪点.
func (captcha *CaptchaImage) DrawTextNoise(complex int) error {
density := 1500
if complex == CaptchaComplexLower {
density = 2000
} else if complex == CaptchaComplexMedium {
density = 1500
} else if complex == CaptchaComplexHigh {
density = 1000
}
maxSize := (captcha.height * captcha.width) / density
r := rand.New(rand.NewSource(time.Now().UnixNano()))
c := freetype.NewContext()
c.SetDPI(*dpi)
c.SetClip(captcha.nrgba.Bounds())
c.SetDst(captcha.nrgba)
c.SetHinting(font.HintingFull)
rawFontSize := float64(captcha.height) / (1 + float64(r.Intn(7))/float64(10))
for i := 0; i < maxSize; i++ {
rw := r.Intn(captcha.width)
rh := r.Intn(captcha.height)
text := RandText(1)
fontSize := rawFontSize/2 + float64(r.Intn(5))
c.SetSrc(image.NewUniform(RandLightColor()))
c.SetFontSize(fontSize)
f, err := RandFontFamily()
if err != nil {
log.Println(err)
return err
}
c.SetFont(f)
pt := freetype.Pt(rw, rh)
_, err = c.DrawString(text, pt)
if err != nil {
log.Println(err)
return err
}
}
return nil
}
//写字.
func (captcha *CaptchaImage) DrawText(text string) error {
c := freetype.NewContext()
c.SetDPI(*dpi)
c.SetClip(captcha.nrgba.Bounds())
c.SetDst(captcha.nrgba)
c.SetHinting(font.HintingFull)
fontWidth := captcha.width / len(text)
for i, s := range text {
fontSize := float64(captcha.height) / (1 + float64(r.Intn(7))/float64(9))
c.SetSrc(image.NewUniform(RandDeepColor()))
c.SetFontSize(fontSize)
f, err := RandFontFamily()
if err != nil {
log.Println(err)
return err
}
c.SetFont(f)
x := int(fontWidth)*i + int(fontWidth)/int(fontSize)
y := 5 + r.Intn(captcha.height/2) + int(fontSize/2)
pt := freetype.Pt(x, y)
_, err = c.DrawString(string(s), pt)
if err != nil {
log.Println(err)
return err
}
//pt.Y += c.PointToFixed(*size * *spacing)
//pt.X += c.PointToFixed(*size);
}
return nil
}
//获取所及字体.
func RandFontFamily() (*truetype.Font, error) {
fontfile := FontFamily[r.Intn(len(FontFamily))]
fontBytes, err := ioutil.ReadFile(fontfile)
if err != nil {
log.Println(err)
return &truetype.Font{}, err
}
f, err := freetype.ParseFont(fontBytes)
if err != nil {
log.Println(err)
return &truetype.Font{}, err
}
return f, nil
}
//随机生成深色系.
func RandDeepColor() color.RGBA {
randColor := RandColor()
increase := float64(30 + r.Intn(255))
red := math.Abs(math.Min(float64(randColor.R)-increase, 255))
green := math.Abs(math.Min(float64(randColor.G)-increase, 255))
blue := math.Abs(math.Min(float64(randColor.B)-increase, 255))
return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)}
}
//随机生成浅色.
func RandLightColor() color.RGBA {
red := r.Intn(55) + 200
green := r.Intn(55) + 200
blue := r.Intn(55) + 200
return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)}
}
//生成随机颜色.
func RandColor() color.RGBA {
red := r.Intn(255)
green := r.Intn(255)
blue := r.Intn(255)
if (red + green) > 400 {
blue = 0
} else {
blue = 400 - green - red
}
if blue > 255 {
blue = 255
}
return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)}
}
//生成随机字体.
func RandText(num int) string {
textNum := len(txtChars)
text := ""
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < num; i++ {
text = text + string(txtChars[r.Intn(textNum)])
}
return text
}
//添加一个字体路径到字体库.
func SetFontFamily(fontPath ...string) {
FontFamily = append(FontFamily, fontPath...)
}
// 颜色代码转换为RGB
//input int
//output int red, green, blue.
func ColorToRGB(colorVal int) color.RGBA {
red := colorVal >> 16
green := (colorVal & 0x00FF00) >> 8
blue := colorVal & 0x0000FF
return color.RGBA{
R: uint8(red),
G: uint8(green),
B: uint8(blue),
A: uint8(255),
}
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化