zoukankan      html  css  js  c++  java
  • golang 防知乎 中文验证码 源码

    原创,转载请注明出处!

    最开始用图形来模仿文字进行各种角度的倒立和排列,后来切换为文字后,有很多问题。总结如下:

    1、程序在画图形和画文字方面不一样,图形的是从原点开始(0,0),而文字则从文字的基线开始(0,baseline)

    2、在增加角度偏移时,文字或图形的高宽会产生变化(偏∠45度时达到最大),这时候为了让它们顶点对齐,需要计算偏移量(用三角函数)

    3、在绘图时,会先旋转“画布”(描述可能不准确),再绘制文字。此时要往回旋转,否则下一个图形会顺着这个角度继续画。

    4、为了让图形保持固定宽度,对于有偏角的文字,需要平均缩小左右间距(否则不同的角度,固定的文字个数,会让图形宽度不同)

    效果图:

    源码:(代码还可以再整理和优化,但限于计划时间,懒得弄了)

    package main
    
    import ( 
    	"math"
    	"reflect"
    	"time"
    	"math/rand"
    	"github.com/golang/freetype/truetype"
    	"github.com/fogleman/gg" // 需要安装这个包
        "flag" 
        "fmt" 
        "io/ioutil" 
        "log" 
    )
    
    var wORDSLIB = []interface{}{"赵","钱","孙","李","周","吴","郑","冯","陈","褚","卫","蒋","沈","韩","杨","朱","秦","尤","许","何","吕","施","张","孔","曹","严","华","金","魏","陶","姜","戚","谢","邹","喻","柏","水","窦","章","云","苏","潘","葛","奚","范","彭","郎","鲁","韦","昌","马","苗","凤","花","方","俞","任","袁","柳","酆","鲍","史","唐","费","廉","岑","薛","雷","贺","倪","汤","滕","殷","罗","毕","郝","邬","安","常","乐","于","时","傅","皮","卞","齐","康","伍","余","元","卜","顾","孟","平","黄","和","穆","萧","尹","姚","邵","湛","汪","祁","毛","禹","狄","米","贝","明","臧","计","伏","成","戴","谈","宋","茅","庞","熊","纪","舒","屈","项","祝","董","梁","杜","阮","蓝","闵","席","季","麻","强","贾","路","娄","危","江","童","颜","郭","梅","盛","林","刁","钟","徐","邱","骆","高","夏","蔡","樊","胡","凌","霍","虞","万","支","柯","昝","管","卢","莫","经","房","裘","缪","干","解","应","宗","丁","宣","贲","邓","郁","单","杭","洪","包","诸","左","石","崔","吉","钮","龚","程","嵇","邢","滑","裴","陆","荣","翁","荀","羊","於","惠","甄","曲","家","封","芮","羿","储","靳","汲","邴","糜","松","井","段","富","巫","乌","焦","巴","弓","牧","隗","山","谷","车","侯","宓","蓬","全","郗","班","仰","秋","仲","伊","宫","宁","仇","栾","暴","甘","钭","厉","戎","祖","武","符","刘","景","詹","束","龙","叶","幸","司","韶","郜","黎","蓟","薄","印","宿","白","怀","蒲","邰","从","鄂","索","咸","籍","赖","卓","蔺","屠","蒙","池","乔","阴","鬱","胥","能","苍","双","闻","莘","党","翟","谭","贡","劳","逄","姬","扶","堵","冉","宰","郦","雍","卻","璩","桑","桂","濮","牛","寿","通","边","扈","燕","冀","郏","浦","尚","农","温","别","庄","晏","柴","瞿","阎","充","慕","连","茹","习","宦","艾","鱼","容","向","古","易","慎","戈","廖","庾","终","暨","居","衡","步","都","耿","满","弘","匡","国","文","寇","广","禄","阙","东","欧","殳","沃","利","蔚","越","夔","隆","师","巩","厍","聂","晁","勾","敖","融","冷","訾","辛","阚","那","简","饶","空","曾","毋","沙","乜","养","鞠","须","丰","巢","关","蒯","相","查","后","荆","红","游","竺","权","逯","盖","益","桓","公","俟","上","官","阳","人","赫","皇","甫","尉","迟","澹","台","冶","政","淳","太","叔","申","轩","辕","令","狐","离","宇","长","鲜","闾","丘","徒","仉","督","子","颛","端","木","西","漆","雕","正","壤","驷","良","拓","跋","夹","父","晋","楚","闫","法","汝","鄢","涂","钦","百","里","南","门","呼","延","归","海","舌","微","生","岳","帅","缑","亢","况","郈","有","琴","商","牟","佘","佴","伯","赏","墨","哈","谯","笪","年","爱","佟","第","五","言","福","姓"}
    
    var ( 
        dpi      = flag.Float64("dpi", 72, "screen resolution in Dots Per Inch") 
        fontfile = flag.String("fontfile", "./SIMYOU.TTF", "filename of the ttf font") 
        size     = flag.Float64("size", 40, "font size in points") 
    )
    
    func main() { 
    	x := "abc"
    	fmt.Println(x[0:3])
        drawImageBygg()
    } 
    
    func drawImageBygg(){
    	dc := gg.NewContext(320, 56)  // 56 => w*sin(45) + h*sin(45)  45度时,字体达到最大高度
    	dc.SetRGBA(1, 1, 1,0) // 设置背景色:末尾为透明度 1-0(1-不透明 0-透明)
    	dc.Clear()
    	dc.SetRGBA(0, 0, 0,1) // 设置字体色
    
    	fontBytes, err := ioutil.ReadFile(*fontfile) 
        if err != nil { 
            log.Println(err) 
            return 
        } 
    	font, err := truetype.Parse(fontBytes)
    	if err != nil {
            log.Println(err) 
            return 
    	}
    	face := truetype.NewFace(font, &truetype.Options{
    		Size: *size,
    		DPI:*dpi,
    	}) 
    	dc.SetFontFace(face)
    
    	// 初始化用于计算坐标的变量
    	fm := face.Metrics()
    	ascent := float64(fm.Ascent.Round()) // 字体的基线到顶部距离
    	decent := float64(fm.Descent.Round()) // 字体的基线到底部的距离
    	w := float64(fm.Height.Round()) // 方块字,大多数应为等宽字,即和高度一样
    	h := float64(fm.Height.Round())
    	totalWidth := 0.0 // 目前已累积的图片宽度(需要用来计算字体位置)
    
    	// 随机取汉字,定位倒立的字
    	words := getRandomMembersFromMemberLibary(wORDSLIB,8) // 取8个字
    	reverseWordsIndex := getRandomMembersFromMemberLibary([]interface{}{0,1,2,3,4,5,6,7},2) // 随机2个倒立字
    
    	for i,word := range words{
    		degree := If(Contain(i,reverseWordsIndex),float64(RandInt64(150,210)),float64(RandInt64(-30,30))) // 随机角度,正向角度 -30~30,倒立角度 150~210
    		x,y,leftCutSize,rightCS := getCoordByQuadrantAndDegree(w,h,ascent,decent,degree,totalWidth)
    		dc.RotateAbout(gg.Radians(degree),0,0)
    		dc.DrawStringAnchored(word.(string), x,y, 0,0)
    		dc.RotateAbout(-1*gg.Radians(degree),0,0)
    		totalWidth = totalWidth + leftCutSize + rightCS
    		fmt.Println("x:",x,"y:",y,"total:",totalWidth,"degree:",degree)
    	}
    
    	dc.Stroke()
    	dc.SavePNG("out.png")
        fmt.Println("Wrote out.png OK.") 
    }
    
    func getCoordByQuadrantAndDegree(w,h,ascent,descent,degree,beforTotalWidth float64)(x,y,leftCutSize,rightCutSize float64){
    	var totalWidth float64
    	switch{
    		case degree<=0 && degree >= -40:  // 第一象限:逆时针 -30度 ~ 0  <=>  330 ~ 360 (目前参数要传入负数)  
    			rd := -1 * degree // 转为正整数,便于计算
    			leftCutSize = w*getDegreeSin(90-rd)
    			rightCutSize = h*getDegreeSin(rd)
    			
    			offset := (leftCutSize + rightCutSize - w) / 2 // 横向偏移量(角度倾斜越厉害,占宽越多,通过偏移量分摊给它的左右边距来收窄)
    			leftCutSize,rightCutSize = leftCutSize - offset,rightCutSize - offset
    
    			totalWidth = beforTotalWidth + leftCutSize 
    			x = getDegreeSin(90 - rd)*totalWidth - w 
    			y = ascent + getDegreeSin(rd)*totalWidth  
    		case degree >=0 && degree <= 40:  // 第四象限:顺时针 0 ~ 30度
    			leftCutSize = h*getDegreeSin(degree)
    			rightCutSize = w*getDegreeSin(90-degree)
    
    			offset := (leftCutSize + rightCutSize - w) / 2
    			leftCutSize,rightCutSize = leftCutSize - offset,rightCutSize - offset
    
    			totalWidth = beforTotalWidth + leftCutSize // 现在totalwidth = 前面的宽 + 自己的左切边
    			x = getDegreeSin(90-degree)*totalWidth
    			y = ascent - getDegreeSin(degree)*totalWidth 
    		case degree >= 180 && degree <= 220:  // 第二象限:顺时针 180 ~ 210度
    			rd := degree - 180
    			leftCutSize = h*getDegreeSin(rd)
    			rightCutSize = w*getDegreeSin(90-rd)
    
    			offset := (leftCutSize + rightCutSize - w) / 2 
    			leftCutSize,rightCutSize = leftCutSize - offset,rightCutSize - offset
    
    			totalWidth = beforTotalWidth + leftCutSize 
    			x = -1 * (getDegreeSin(90-rd)*totalWidth + w)
    			y = getDegreeSin(rd)*totalWidth - descent
    		case degree >= 140 && degree <= 180:  // 第三象限:顺时针 150 ~ 180度
    			rd := 180-degree
    			leftCutSize = w*getDegreeSin(90-rd)
    			rightCutSize = h*getDegreeSin(rd)
    
    			offset := (leftCutSize + rightCutSize - w) / 2
    			leftCutSize,rightCutSize = leftCutSize - offset,rightCutSize - offset
    			
    			totalWidth = beforTotalWidth + leftCutSize 
    			x = -1 * (getDegreeSin(90-rd) * totalWidth)
    			y = -1 * (getDegreeSin(rd) * totalWidth + descent)
    		default: panic(fmt.Sprintf("非法的参数:%f",degree))
    	}
    	return
    }
    
    func getDegreeSin(degree float64) float64{
    	return math.Sin(degree*math.Pi/180)
    }
    
    //RandInt64 ...
    func RandInt64(min, max int64) int64 {
    	if min >= max || min == 0 || max == 0 {
    		return max
    	}
    	rand.Seed(time.Now().UnixNano())
    	return rand.Int63n(max-min) + min
    }
    
    func getRandomMembersFromMemberLibary(lib []interface{},size int)[]interface{}{
    	source,result := make([]interface{},0),make([]interface{},0)
    	if size <= 0 || len(lib) == 0{
    		return result
    	}
    	for _,v := range lib{
    		source = append(source,v)
    	}
    	if size >= len(lib){
    		return source
    	}
    	for i:=0;i<size;i++{
    		rand.Seed(time.Now().UnixNano())
    		pos := rand.Intn(len(source))
    		result = append(result,source[pos])
    		source = append(source[:pos], source[pos+1:]...)
    	}
    	return result
    }
    
    //Contain ...
    func Contain(obj interface{}, target interface{}) bool {
        targetValue := reflect.ValueOf(target)
        switch reflect.TypeOf(target).Kind() {
        case reflect.Slice, reflect.Array:
            for i := 0; i < targetValue.Len(); i++ {
                if targetValue.Index(i).Interface() == obj {
                    return true
                }
            }
        case reflect.Map:
            if targetValue.MapIndex(reflect.ValueOf(obj)).IsValid() {
                return true
            }
        }
    
        return false
    }
    
    //If ...
    func If(expr bool,trueVal float64,falseVal float64) float64{
    	if expr {
    		return trueVal 
    	}
    	return falseVal
    }
    

      

     需要一个字体文件,这里使用的幼圆(幼圆免费,其他免费字体懒得找)。

    注意:字体不同,宽度和高度可能会需要调整。

  • 相关阅读:
    METHODS OF AND APPARATUS FOR USING TEXTURES IN GRAPHICS PROCESSING SYSTEMS
    Display controller
    Graphics processing architecture employing a unified shader
    Graphics-Processing Architecture Based on Approximate Rendering
    Architectures for concurrent graphics processing operations
    Procedural graphics architectures and techniques
    DYNAMIC CONTEXT SWITCHING BETWEEN ARCHITECTURALLY DISTINCT GRAPHICS PROCESSORS
    Thermal zone monitoring in an electronic device
    System and method for dynamically adjusting to CPU performance changes
    Framework for Graphics Animation and Compositing Operations
  • 原文地址:https://www.cnblogs.com/Denny_Yang/p/9317538.html
Copyright © 2011-2022 走看看