init
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Andrew Bosonchenko
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
package captcha
|
||||
|
||||
// Bilinear Interpolation 双线性插值
|
||||
// 引用自 code.google.com/p/graphics-go/interp
|
||||
// 主要处理旋转验证码后消除锯齿
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
)
|
||||
|
||||
var bili = Bilinear{}
|
||||
|
||||
type Bilinear struct{}
|
||||
|
||||
func (Bilinear) RGBA(src *image.RGBA, x, y float64) color.RGBA {
|
||||
p := findLinearSrc(src.Bounds(), x, y)
|
||||
|
||||
// Array offsets for the surrounding pixels.
|
||||
off00 := offRGBA(src, p.low.X, p.low.Y)
|
||||
off01 := offRGBA(src, p.high.X, p.low.Y)
|
||||
off10 := offRGBA(src, p.low.X, p.high.Y)
|
||||
off11 := offRGBA(src, p.high.X, p.high.Y)
|
||||
|
||||
var fr, fg, fb, fa float64
|
||||
|
||||
fr += float64(src.Pix[off00+0]) * p.frac00
|
||||
fg += float64(src.Pix[off00+1]) * p.frac00
|
||||
fb += float64(src.Pix[off00+2]) * p.frac00
|
||||
fa += float64(src.Pix[off00+3]) * p.frac00
|
||||
|
||||
fr += float64(src.Pix[off01+0]) * p.frac01
|
||||
fg += float64(src.Pix[off01+1]) * p.frac01
|
||||
fb += float64(src.Pix[off01+2]) * p.frac01
|
||||
fa += float64(src.Pix[off01+3]) * p.frac01
|
||||
|
||||
fr += float64(src.Pix[off10+0]) * p.frac10
|
||||
fg += float64(src.Pix[off10+1]) * p.frac10
|
||||
fb += float64(src.Pix[off10+2]) * p.frac10
|
||||
fa += float64(src.Pix[off10+3]) * p.frac10
|
||||
|
||||
fr += float64(src.Pix[off11+0]) * p.frac11
|
||||
fg += float64(src.Pix[off11+1]) * p.frac11
|
||||
fb += float64(src.Pix[off11+2]) * p.frac11
|
||||
fa += float64(src.Pix[off11+3]) * p.frac11
|
||||
|
||||
var c color.RGBA
|
||||
c.R = uint8(fr + 0.5)
|
||||
c.G = uint8(fg + 0.5)
|
||||
c.B = uint8(fb + 0.5)
|
||||
c.A = uint8(fa + 0.5)
|
||||
return c
|
||||
}
|
||||
|
||||
type BilinearSrc struct {
|
||||
// Top-left and bottom-right interpolation sources
|
||||
low, high image.Point
|
||||
// Fraction of each pixel to take. The 0 suffix indicates
|
||||
// top/left, and the 1 suffix indicates bottom/right.
|
||||
frac00, frac01, frac10, frac11 float64
|
||||
}
|
||||
|
||||
func findLinearSrc(b image.Rectangle, sx, sy float64) BilinearSrc {
|
||||
maxX := float64(b.Max.X)
|
||||
maxY := float64(b.Max.Y)
|
||||
minX := float64(b.Min.X)
|
||||
minY := float64(b.Min.Y)
|
||||
lowX := math.Floor(sx - 0.5)
|
||||
lowY := math.Floor(sy - 0.5)
|
||||
if lowX < minX {
|
||||
lowX = minX
|
||||
}
|
||||
if lowY < minY {
|
||||
lowY = minY
|
||||
}
|
||||
|
||||
highX := math.Ceil(sx - 0.5)
|
||||
highY := math.Ceil(sy - 0.5)
|
||||
if highX >= maxX {
|
||||
highX = maxX - 1
|
||||
}
|
||||
if highY >= maxY {
|
||||
highY = maxY - 1
|
||||
}
|
||||
|
||||
// In the variables below, the 0 suffix indicates top/left, and the
|
||||
// 1 suffix indicates bottom/right.
|
||||
|
||||
// Center of each surrounding pixel.
|
||||
x00 := lowX + 0.5
|
||||
y00 := lowY + 0.5
|
||||
x01 := highX + 0.5
|
||||
y01 := lowY + 0.5
|
||||
x10 := lowX + 0.5
|
||||
y10 := highY + 0.5
|
||||
x11 := highX + 0.5
|
||||
y11 := highY + 0.5
|
||||
|
||||
p := BilinearSrc{
|
||||
low: image.Pt(int(lowX), int(lowY)),
|
||||
high: image.Pt(int(highX), int(highY)),
|
||||
}
|
||||
|
||||
// Literally, edge cases. If we are close enough to the edge of
|
||||
// the image, curtail the interpolation sources.
|
||||
if lowX == highX && lowY == highY {
|
||||
p.frac00 = 1.0
|
||||
} else if sy-minY <= 0.5 && sx-minX <= 0.5 {
|
||||
p.frac00 = 1.0
|
||||
} else if maxY-sy <= 0.5 && maxX-sx <= 0.5 {
|
||||
p.frac11 = 1.0
|
||||
} else if sy-minY <= 0.5 || lowY == highY {
|
||||
p.frac00 = x01 - sx
|
||||
p.frac01 = sx - x00
|
||||
} else if sx-minX <= 0.5 || lowX == highX {
|
||||
p.frac00 = y10 - sy
|
||||
p.frac10 = sy - y00
|
||||
} else if maxY-sy <= 0.5 {
|
||||
p.frac10 = x11 - sx
|
||||
p.frac11 = sx - x10
|
||||
} else if maxX-sx <= 0.5 {
|
||||
p.frac01 = y11 - sy
|
||||
p.frac11 = sy - y01
|
||||
} else {
|
||||
p.frac00 = (x01 - sx) * (y10 - sy)
|
||||
p.frac01 = (sx - x00) * (y11 - sy)
|
||||
p.frac10 = (x11 - sx) * (sy - y00)
|
||||
p.frac11 = (sx - x10) * (sy - y01)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func offRGBA(src *image.RGBA, x, y int) int {
|
||||
return (y-src.Rect.Min.Y)*src.Stride + (x-src.Rect.Min.X)*4
|
||||
}
|
||||
+260
@@ -0,0 +1,260 @@
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"github.com/golang/freetype"
|
||||
"github.com/golang/freetype/truetype"
|
||||
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Captcha struct {
|
||||
frontColors []color.Color
|
||||
bkgColors []color.Color
|
||||
disturlvl DisturLevel
|
||||
fonts []*truetype.Font
|
||||
size image.Point
|
||||
}
|
||||
|
||||
type StrType int
|
||||
|
||||
const (
|
||||
NUM StrType = iota // 数字
|
||||
LOWER // 小写字母
|
||||
UPPER // 大写字母
|
||||
ALL // 全部
|
||||
)
|
||||
|
||||
type DisturLevel int
|
||||
|
||||
const (
|
||||
NORMAL DisturLevel = 4
|
||||
MEDIUM DisturLevel = 8
|
||||
HIGH DisturLevel = 16
|
||||
)
|
||||
|
||||
func New() *Captcha {
|
||||
c := &Captcha{
|
||||
disturlvl: NORMAL,
|
||||
size: image.Point{82, 32},
|
||||
}
|
||||
c.frontColors = []color.Color{color.Black}
|
||||
c.bkgColors = []color.Color{color.White}
|
||||
return c
|
||||
}
|
||||
|
||||
// AddFont 添加一个字体
|
||||
func (c *Captcha) AddFont(path string) error {
|
||||
fontdata, erro := ioutil.ReadFile(path)
|
||||
if erro != nil {
|
||||
return erro
|
||||
}
|
||||
font, erro := freetype.ParseFont(fontdata)
|
||||
if erro != nil {
|
||||
return erro
|
||||
}
|
||||
if c.fonts == nil {
|
||||
c.fonts = []*truetype.Font{}
|
||||
}
|
||||
c.fonts = append(c.fonts, font)
|
||||
return nil
|
||||
}
|
||||
|
||||
//AddFontFromBytes allows to load font from slice of bytes, for example, load the font packed by https://github.com/jteeuwen/go-bindata
|
||||
func (c *Captcha) AddFontFromBytes(contents []byte) error {
|
||||
font, err := freetype.ParseFont(contents)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.fonts == nil {
|
||||
c.fonts = []*truetype.Font{}
|
||||
}
|
||||
c.fonts = append(c.fonts, font)
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetFont 设置字体 可以设置多个
|
||||
func (c *Captcha) SetFont(paths ...string) error {
|
||||
for _, v := range paths {
|
||||
if erro := c.AddFont(v); erro != nil {
|
||||
return erro
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Captcha) SetDisturbance(d DisturLevel) {
|
||||
if d > 0 {
|
||||
c.disturlvl = d
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Captcha) SetFrontColor(colors ...color.Color) {
|
||||
if len(colors) > 0 {
|
||||
c.frontColors = c.frontColors[:0]
|
||||
for _, v := range colors {
|
||||
c.frontColors = append(c.frontColors, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Captcha) SetBkgColor(colors ...color.Color) {
|
||||
if len(colors) > 0 {
|
||||
c.bkgColors = c.bkgColors[:0]
|
||||
for _, v := range colors {
|
||||
c.bkgColors = append(c.bkgColors, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Captcha) SetSize(w, h int) {
|
||||
if w < 48 {
|
||||
w = 48
|
||||
}
|
||||
if h < 20 {
|
||||
h = 20
|
||||
}
|
||||
c.size = image.Point{w, h}
|
||||
}
|
||||
|
||||
func (c *Captcha) randFont() *truetype.Font {
|
||||
return c.fonts[rand.Intn(len(c.fonts))]
|
||||
}
|
||||
|
||||
// 绘制背景
|
||||
func (c *Captcha) drawBkg(img *Image) {
|
||||
ra := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
//填充主背景色
|
||||
bgcolorindex := ra.Intn(len(c.bkgColors))
|
||||
bkg := image.NewUniform(c.bkgColors[bgcolorindex])
|
||||
img.FillBkg(bkg)
|
||||
}
|
||||
|
||||
// 绘制噪点
|
||||
func (c *Captcha) drawNoises(img *Image) {
|
||||
ra := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
// 待绘制图片的尺寸
|
||||
size := img.Bounds().Size()
|
||||
dlen := int(c.disturlvl)
|
||||
// 绘制干扰斑点
|
||||
for i := 0; i < dlen; i++ {
|
||||
x := ra.Intn(size.X)
|
||||
y := ra.Intn(size.Y)
|
||||
r := ra.Intn(size.Y/20) + 1
|
||||
colorindex := ra.Intn(len(c.frontColors))
|
||||
img.DrawCircle(x, y, r, i%4 != 0, c.frontColors[colorindex])
|
||||
}
|
||||
|
||||
// 绘制干扰线
|
||||
for i := 0; i < dlen; i++ {
|
||||
x := ra.Intn(size.X)
|
||||
y := ra.Intn(size.Y)
|
||||
o := int(math.Pow(-1, float64(i)))
|
||||
w := ra.Intn(size.Y) * o
|
||||
h := ra.Intn(size.Y/10) * o
|
||||
colorindex := ra.Intn(len(c.frontColors))
|
||||
img.DrawLine(x, y, x+w, y+h, c.frontColors[colorindex])
|
||||
colorindex++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 绘制文字
|
||||
func (c *Captcha) drawString(img *Image, str string) {
|
||||
|
||||
if c.fonts == nil {
|
||||
panic("没有设置任何字体")
|
||||
}
|
||||
tmp := NewImage(c.size.X, c.size.Y)
|
||||
|
||||
// 文字大小为图片高度的 0.6
|
||||
fsize := int(float64(c.size.Y) * 0.6)
|
||||
// 用于生成随机角度
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
// 文字之间的距离
|
||||
// 左右各留文字的1/4大小为内部边距
|
||||
padding := fsize / 4
|
||||
gap := (c.size.X - padding*2) / (len(str))
|
||||
|
||||
// 逐个绘制文字到图片上
|
||||
for i, char := range str {
|
||||
// 创建单个文字图片
|
||||
// 以文字为尺寸创建正方形的图形
|
||||
str := NewImage(fsize, fsize)
|
||||
// str.FillBkg(image.NewUniform(color.Black))
|
||||
// 随机取一个前景色
|
||||
colorindex := r.Intn(len(c.frontColors))
|
||||
|
||||
//随机取一个字体
|
||||
font := c.randFont()
|
||||
str.DrawString(font, c.frontColors[colorindex], string(char), float64(fsize))
|
||||
|
||||
// 转换角度后的文字图形
|
||||
rs := str.Rotate(float64(r.Intn(40) - 20))
|
||||
// 计算文字位置
|
||||
s := rs.Bounds().Size()
|
||||
left := i*gap + padding
|
||||
top := (c.size.Y - s.Y) / 2
|
||||
// 绘制到图片上
|
||||
draw.Draw(tmp, image.Rect(left, top, left+s.X, top+s.Y), rs, image.ZP, draw.Over)
|
||||
}
|
||||
if c.size.Y >= 48 {
|
||||
// 高度大于48添加波纹 小于48波纹影响用户识别
|
||||
tmp.distortTo(float64(fsize)/10, 200.0)
|
||||
}
|
||||
|
||||
draw.Draw(img, tmp.Bounds(), tmp, image.ZP, draw.Over)
|
||||
}
|
||||
|
||||
// Create 生成一个验证码图片
|
||||
func (c *Captcha) Create(num int, t StrType) (*Image, string) {
|
||||
if num <= 0 {
|
||||
num = 4
|
||||
}
|
||||
dst := NewImage(c.size.X, c.size.Y)
|
||||
//tmp := NewImage(c.size.X, c.size.Y)
|
||||
c.drawBkg(dst)
|
||||
c.drawNoises(dst)
|
||||
|
||||
str := string(c.randStr(num, int(t)))
|
||||
c.drawString(dst, str)
|
||||
//c.drawString(tmp, str)
|
||||
|
||||
return dst, str
|
||||
}
|
||||
|
||||
func (c *Captcha) CreateCustom(str string) *Image {
|
||||
if len(str) == 0 {
|
||||
str = "unkown"
|
||||
}
|
||||
dst := NewImage(c.size.X, c.size.Y)
|
||||
c.drawBkg(dst)
|
||||
c.drawNoises(dst)
|
||||
c.drawString(dst, str)
|
||||
return dst
|
||||
}
|
||||
|
||||
var fontKinds = [][]int{[]int{10, 48}, []int{26, 97}, []int{26, 65}}
|
||||
|
||||
// 生成随机字符串
|
||||
// size 个数 kind 模式
|
||||
func (c *Captcha) randStr(size int, kind int) []byte {
|
||||
ikind, result := kind, make([]byte, size)
|
||||
isAll := kind > 2 || kind < 0
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
for i := 0; i < size; i++ {
|
||||
if isAll {
|
||||
ikind = rand.Intn(3)
|
||||
}
|
||||
scope, base := fontKinds[ikind][0], fontKinds[ikind][1]
|
||||
result[i] = uint8(base + rand.Intn(scope))
|
||||
}
|
||||
return result
|
||||
}
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"math"
|
||||
|
||||
"github.com/golang/freetype"
|
||||
"github.com/golang/freetype/truetype"
|
||||
)
|
||||
|
||||
// Image 图片
|
||||
type Image struct {
|
||||
*image.RGBA
|
||||
}
|
||||
|
||||
// NewImage 创建一个新的图片
|
||||
func NewImage(w, h int) *Image {
|
||||
img := &Image{image.NewRGBA(image.Rect(0, 0, w, h))}
|
||||
return img
|
||||
}
|
||||
|
||||
func sign(x int) int {
|
||||
if x > 0 {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// DrawLine 画直线
|
||||
// Bresenham算法(https://zh.wikipedia.org/zh-cn/布雷森漢姆直線演算法)
|
||||
// x1,y1 起点 x2,y2终点
|
||||
func (img *Image) DrawLine(x1, y1, x2, y2 int, c color.Color) {
|
||||
dx, dy, flag := int(math.Abs(float64(x2-x1))),
|
||||
int(math.Abs(float64(y2-y1))),
|
||||
false
|
||||
if dy > dx {
|
||||
flag = true
|
||||
x1, y1 = y1, x1
|
||||
x2, y2 = y2, x2
|
||||
dx, dy = dy, dx
|
||||
}
|
||||
ix, iy := sign(x2-x1), sign(y2-y1)
|
||||
n2dy := dy * 2
|
||||
n2dydx := (dy - dx) * 2
|
||||
d := n2dy - dx
|
||||
for x1 != x2 {
|
||||
if d < 0 {
|
||||
d += n2dy
|
||||
} else {
|
||||
y1 += iy
|
||||
d += n2dydx
|
||||
}
|
||||
if flag {
|
||||
img.Set(y1, x1, c)
|
||||
} else {
|
||||
img.Set(x1, y1, c)
|
||||
}
|
||||
x1 += ix
|
||||
}
|
||||
}
|
||||
|
||||
func (img *Image) drawCircle8(xc, yc, x, y int, c color.Color) {
|
||||
img.Set(xc+x, yc+y, c)
|
||||
img.Set(xc-x, yc+y, c)
|
||||
img.Set(xc+x, yc-y, c)
|
||||
img.Set(xc-x, yc-y, c)
|
||||
img.Set(xc+y, yc+x, c)
|
||||
img.Set(xc-y, yc+x, c)
|
||||
img.Set(xc+y, yc-x, c)
|
||||
img.Set(xc-y, yc-x, c)
|
||||
}
|
||||
|
||||
// DrawCircle 画圆
|
||||
// xc,yc 圆心坐标 r 半径 fill是否填充颜色
|
||||
func (img *Image) DrawCircle(xc, yc, r int, fill bool, c color.Color) {
|
||||
size := img.Bounds().Size()
|
||||
// 如果圆在图片可见区域外,直接退出
|
||||
if xc+r < 0 || xc-r >= size.X || yc+r < 0 || yc-r >= size.Y {
|
||||
return
|
||||
}
|
||||
x, y, d := 0, r, 3-2*r
|
||||
for x <= y {
|
||||
if fill {
|
||||
for yi := x; yi <= y; yi++ {
|
||||
img.drawCircle8(xc, yc, x, yi, c)
|
||||
}
|
||||
} else {
|
||||
img.drawCircle8(xc, yc, x, y, c)
|
||||
}
|
||||
if d < 0 {
|
||||
d = d + 4*x + 6
|
||||
} else {
|
||||
d = d + 4*(x-y) + 10
|
||||
y--
|
||||
}
|
||||
x++
|
||||
}
|
||||
}
|
||||
|
||||
// DrawString 写字
|
||||
func (img *Image) DrawString(font *truetype.Font, c color.Color, str string, fontsize float64) {
|
||||
ctx := freetype.NewContext()
|
||||
// default 72dpi
|
||||
ctx.SetDst(img)
|
||||
ctx.SetClip(img.Bounds())
|
||||
ctx.SetSrc(image.NewUniform(c))
|
||||
ctx.SetFontSize(fontsize)
|
||||
ctx.SetFont(font)
|
||||
// 写入文字的位置
|
||||
pt := freetype.Pt(0, int(-fontsize/6)+ctx.PointToFixed(fontsize).Ceil())
|
||||
ctx.DrawString(str, pt)
|
||||
}
|
||||
|
||||
// Rotate 旋转
|
||||
func (img *Image) Rotate(angle float64) image.Image {
|
||||
return new(rotate).Rotate(angle, img.RGBA).transformRGBA()
|
||||
}
|
||||
|
||||
// 填充背景
|
||||
func (img *Image) FillBkg(c image.Image) {
|
||||
draw.Draw(img, img.Bounds(), c, image.ZP, draw.Over)
|
||||
}
|
||||
|
||||
// 水波纹, amplude=振幅, period=周期
|
||||
// copy from https://github.com/dchest/captcha/blob/master/image.go
|
||||
func (img *Image) distortTo(amplude float64, period float64) {
|
||||
w := img.Bounds().Max.X
|
||||
h := img.Bounds().Max.Y
|
||||
|
||||
oldm := img.RGBA
|
||||
|
||||
dx := 1.4 * math.Pi / period
|
||||
for x := 0; x < w; x++ {
|
||||
for y := 0; y < h; y++ {
|
||||
xo := amplude * math.Sin(float64(y)*dx)
|
||||
yo := amplude * math.Cos(float64(x)*dx)
|
||||
rgba := oldm.RGBAAt(x+int(xo), y+int(yo))
|
||||
if rgba.A > 0 {
|
||||
oldm.SetRGBA(x, y, rgba)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func inBounds(b image.Rectangle, x, y float64) bool {
|
||||
if x < float64(b.Min.X) || x >= float64(b.Max.X) {
|
||||
return false
|
||||
}
|
||||
if y < float64(b.Min.Y) || y >= float64(b.Max.Y) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type rotate struct {
|
||||
dx float64
|
||||
dy float64
|
||||
sin float64
|
||||
cos float64
|
||||
neww float64
|
||||
newh float64
|
||||
src *image.RGBA
|
||||
}
|
||||
|
||||
func radian(angle float64) float64 {
|
||||
return angle * math.Pi / 180.0
|
||||
}
|
||||
|
||||
func (r *rotate) Rotate(angle float64, src *image.RGBA) *rotate {
|
||||
r.src = src
|
||||
srsize := src.Bounds().Size()
|
||||
width, height := srsize.X, srsize.Y
|
||||
|
||||
// 源图四个角的坐标(以图像中心为坐标系原点)
|
||||
// 左下角,右下角,左上角,右上角
|
||||
srcwp, srchp := float64(width)*0.5, float64(height)*0.5
|
||||
srcx1, srcy1 := -srcwp, srchp
|
||||
srcx2, srcy2 := srcwp, srchp
|
||||
srcx3, srcy3 := -srcwp, -srchp
|
||||
srcx4, srcy4 := srcwp, -srchp
|
||||
|
||||
r.sin, r.cos = math.Sincos(radian(angle))
|
||||
// 旋转后的四角坐标
|
||||
desx1, desy1 := r.cos*srcx1+r.sin*srcy1, -r.sin*srcx1+r.cos*srcy1
|
||||
desx2, desy2 := r.cos*srcx2+r.sin*srcy2, -r.sin*srcx2+r.cos*srcy2
|
||||
desx3, desy3 := r.cos*srcx3+r.sin*srcy3, -r.sin*srcx3+r.cos*srcy3
|
||||
desx4, desy4 := r.cos*srcx4+r.sin*srcy4, -r.sin*srcx4+r.cos*srcy4
|
||||
|
||||
// 新的高度很宽度
|
||||
r.neww = math.Max(math.Abs(desx4-desx1), math.Abs(desx3-desx2)) + 0.5
|
||||
r.newh = math.Max(math.Abs(desy4-desy1), math.Abs(desy3-desy2)) + 0.5
|
||||
r.dx = -0.5*r.neww*r.cos - 0.5*r.newh*r.sin + srcwp
|
||||
r.dy = 0.5*r.neww*r.sin - 0.5*r.newh*r.cos + srchp
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *rotate) pt(x, y int) (float64, float64) {
|
||||
return float64(-y)*r.sin + float64(x)*r.cos + r.dy,
|
||||
float64(y)*r.cos + float64(x)*r.sin + r.dx
|
||||
}
|
||||
|
||||
func (r *rotate) transformRGBA() image.Image {
|
||||
|
||||
srcb := r.src.Bounds()
|
||||
b := image.Rect(0, 0, int(r.neww), int(r.newh))
|
||||
dst := image.NewRGBA(b)
|
||||
|
||||
for y := b.Min.Y; y < b.Max.Y; y++ {
|
||||
for x := b.Min.X; x < b.Max.X; x++ {
|
||||
sx, sy := r.pt(x, y)
|
||||
if inBounds(srcb, sx, sy) {
|
||||
// 消除锯齿填色
|
||||
c := bili.RGBA(r.src, sx, sy)
|
||||
off := (y-dst.Rect.Min.Y)*dst.Stride + (x-dst.Rect.Min.X)*4
|
||||
dst.Pix[off+0] = c.R
|
||||
dst.Pix[off+1] = c.G
|
||||
dst.Pix[off+2] = c.B
|
||||
dst.Pix[off+3] = c.A
|
||||
}
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# golang实现的验证码 golang captcha
|
||||
|
||||
|
||||
|
||||
## 优点
|
||||
|
||||
1. 使用简单
|
||||
2. 不依赖第三方图形库 直接go get 就Ok
|
||||
3. 丰富自定义设置(字体,多颜色,验证码大小,文字模式,文字数量,干扰强度)
|
||||
|
||||
|
||||
|
||||
## demo
|
||||
|
||||

|
||||
|
||||
## 使用 Start using it
|
||||
|
||||
Download and install it:
|
||||
```
|
||||
go get github.com/afocus/captcha
|
||||
```
|
||||
**必须设置font**
|
||||
|
||||
#### 最简单的示例 sample use
|
||||
|
||||
```go
|
||||
cap = captcha.New()
|
||||
// 设置字体
|
||||
cap.SetFont("comic.ttf")
|
||||
// 创建验证码 4个字符 captcha.NUM 字符模式数字类型
|
||||
// 返回验证码图像对象以及验证码字符串 后期可以对字符串进行对比 判断验证
|
||||
img,str := cap.Create(4,captcha.NUM)
|
||||
```
|
||||
|
||||
#### 设置 set options
|
||||
|
||||
```go
|
||||
cap = captcha.New()
|
||||
// 可以设置多个字体 或使用cap.AddFont("xx.ttf")追加
|
||||
cap.SetFont("comic.ttf", "xxx.ttf")
|
||||
// 设置验证码大小
|
||||
cap.SetSize(128, 64)
|
||||
// 设置干扰强度
|
||||
cap.SetDisturbance(captcha.MEDIUM)
|
||||
// 设置前景色 可以多个 随机替换文字颜色 默认黑色
|
||||
cap.SetFrontColor(color.RGBA{255, 255, 255, 255})
|
||||
// 设置背景色 可以多个 随机替换背景色 默认白色
|
||||
cap.SetBkgColor(color.RGBA{255, 0, 0, 255}, color.RGBA{0, 0, 255, 255}, color.RGBA{0, 153, 0, 255})
|
||||
|
||||
img,str := cap.Create(4,captcha.NUM)
|
||||
img1,str1 := cap.Create(6,captcha.ALL)
|
||||
```
|
||||
|
||||
#### 自定义字符串 custom captcha words
|
||||
|
||||
```go
|
||||
cap = captcha.New()
|
||||
// 设置字体
|
||||
cap.SetFont("comic.ttf")
|
||||
img := cap.CreateCustom("hello")
|
||||
```
|
||||
|
||||
|
||||
#### 网站中如果使用? how to use for web
|
||||
|
||||
look `examples/main.go`
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user