• [go]gg库绘图与添加文字



    gg库是一个功能强大的图片处理库,提供了画圆、画方、画线、填充、描边、旋转、缩放、文字处理、剪切、蒙版、翻转的接口。

    gg库

    要使用gg库,需要先安装go get -u github.com/fogleman/gg。对应的接口说明文档https://pkg.go.dev/github.com/fogleman/gg

    如:通过椭圆画花:

    import "github.com/fogleman/gg"
    
    func drawFlower() {
    	const S = 1024
    	dc := gg.NewContext(S, S)
    	dc.SetRGBA(0, 0, 0, 0.1)
    	for i := 0; i < 360; i += 15 {
    		dc.Push()
    		dc.RotateAbout(gg.Radians(float64(i)), S/2, S/2)
    		dc.DrawEllipse(S/2, S/2, S*7/16, S/8)
    		dc.Fill()
    		dc.Pop()
    	}
    	dc.SavePNG("D:\\temp\\out.png")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    创建context

    在使用库前,需要先创建一个context:

    NewContext(width, height int) *Context
    NewContextForImage(im image.Image) *Context
    NewContextForRGBA(im *image.RGBA) *Context
    
    • 1
    • 2
    • 3

    Draw函数

    一系列常见图形函数:

    DrawPoint(x, y, r float64)
    DrawLine(x1, y1, x2, y2 float64)
    DrawRectangle(x, y, w, h float64)
    DrawRoundedRectangle(x, y, w, h, r float64)
    DrawCircle(x, y, r float64)
    DrawArc(x, y, r, angle1, angle2 float64)
    DrawEllipse(x, y, rx, ry float64)
    DrawEllipticalArc(x, y, rx, ry, angle1, angle2 float64)
    DrawRegularPolygon(n int, x, y, r, rotation float64)
    DrawImage(im image.Image, x, y int)
    DrawImageAnchored(im image.Image, x, y int, ax, ay float64)
    SetPixel(x, y int)
    
    MoveTo(x, y float64)
    LineTo(x, y float64)
    QuadraticTo(x1, y1, x2, y2 float64)
    CubicTo(x1, y1, x2, y2, x3, y3 float64)
    ClosePath()
    ClearPath()
    NewSubPath()
    
    Clear() // Fill entire image with current color
    Stroke()
    Fill()
    StrokePreserve()
    FillPreserve()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    DrawXXX函数只是创建了一系列的路径,只有调用Stoke函数后,才会使用当前设定(颜色、线宽等)完成实际的画图动作。

    Text函数

    在图片上添加文字:

    DrawString(s string, x, y float64)
    DrawStringAnchored(s string, x, y, ax, ay float64)
    DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align)
    MeasureString(s string) (w, h float64)
    MeasureMultilineString(s string, lineSpacing float64) (w, h float64)
    WordWrap(s string, w float64) []string
    SetFontFace(fontFace font.Face)
    LoadFontFace(path string, points float64) error
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    Color函数

    设定颜色的函数:

    SetRGB(r, g, b float64)
    SetRGBA(r, g, b, a float64)
    SetRGB255(r, g, b int)
    SetRGBA255(r, g, b, a int)
    SetColor(c color.Color)
    SetHexColor(x string)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    线与填充函数

    设定线与填充:

    SetLineWidth(lineWidth float64)
    SetLineCap(lineCap LineCap)
    SetLineJoin(lineJoin LineJoin)
    SetDash(dashes ...float64)
    SetDashOffset(offset float64)
    SetFillRule(fillRule FillRule)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    设定渐变与样式

    SetFillStyle(pattern Pattern)
    SetStrokeStyle(pattern Pattern)
    NewSolidPattern(color color.Color)
    NewLinearGradient(x0, y0, x1, y1 float64)
    NewRadialGradient(x0, y0, r0, x1, y1, r1 float64)
    NewSurfacePattern(im image.Image, op RepeatOp)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    转换函数

    各转换函数:

    Identity()
    Translate(x, y float64)
    Scale(x, y float64)
    Rotate(angle float64)
    Shear(x, y float64)
    ScaleAbout(sx, sy, x, y float64)
    RotateAbout(angle, x, y float64)
    ShearAbout(sx, sy, x, y float64)
    TransformPoint(x, y float64) (tx, ty float64)
    InvertY()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    裁剪函数

    裁剪相关函数:

    Clip() // 更新裁剪区:更新为,当前Path范围与裁剪区的交集
    ClipPreserve()
    ResetClip()
    AsMask() *image.Alpha
    SetMask(mask *image.Alpha)
    InvertMask()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    辅助函数

    Radians(degrees float64) float64
    Degrees(radians float64) float64
    LoadImage(path string) (image.Image, error)
    
    LoadPNG(path string) (image.Image, error)
    SavePNG(path string, im image.Image) error
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    保存当前context的状态:

    Push()
    Pop()
    
    • 1
    • 2

    绘图

    在使用库之前需要先初始化一个Context对象。
    通过draw函数画图时,只有调用Stroke(或Fill)函数后,才会真正画(使用当前设置);

    如:画一条蓝色的线,边框为蓝色的矩形;以及一条红色的线,填充为红色的矩形。
    若把Stroke-1处的Stoke注释掉,则:

    • 蓝色的线将不会画出:因Fill-2会填充所有闭合路径后,清除路径信息;
    • 蓝色的矩形框会变为填充红色的矩形;
    func drawShapes() {
    	width := 300
    	height := 300
    	dc := gg.NewContext(width, height)
    
    	// blue line
    	pos := 50.0
    	dc.SetRGB255(0, 0, 255)
    	dc.SetLineWidth(3)
    
    	dc.DrawRectangle(10, 190, 100, 100)
    	//dc.Fill()
    
    	dc.DrawLine(0, pos, float64(width), pos)
    	dc.Stroke()	// Stroke-1
    
    	// red line
    	pos += 100
    	dc.SetRGB255(255, 0, 0)
    	dc.SetLineWidth(2)
    
    	dc.DrawRectangle(150, 190, 100, 100)
    	dc.Fill()	// Fill-2
    
    	dc.DrawLine(0, pos, float64(width), pos)
    	dc.Stroke()
    
    	dc.SavePNG("D:\\temp\\out.png")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    输出的图片样式:
    在这里插入图片描述

    裁剪

    裁剪图片为圆形:

    func drawCircleImg() {
    	img, err := gg.LoadImage("D:\\temp\\tmp.jpg")
    	if err != nil {
    		panic(err)
    	}
    
    	dc := gg.NewContext(img.Bounds().Dx(), img.Bounds().Dy())
    
    	// 画圆形
    	radius := math.Min(float64(img.Bounds().Dx()), float64(img.Bounds().Dy())) / 2
    	dc.DrawCircle(float64(img.Bounds().Dx()/2), float64(img.Bounds().Dy()/2), radius)
    
    	// 对画布进行裁剪
    	dc.Clip()
    
    	dc.DrawImage(img, 0, 0)
    	dc.SavePNG("D:\\temp\\out.png")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    旋转

    旋转使用Rotate()(绕左上角) 或 RotateAbout(绕指定点):

    • 旋转是顺时针方向;
    • 参数是弧度,需要通过Radians把角度转换为弧度;
    func rotateImage() {
    	img, err := gg.LoadImage("D:\\temp\\tmp.jpg")
    	if err != nil {
    		panic(err)
    	}
    
    	width := 2 * img.Bounds().Dx()
    	height := 2 * img.Bounds().Dy()
    
    	dc := gg.NewContext(width, height)
    	dc.DrawRectangle(0, 0, float64(width), float64(width))
    	dc.SetRGB255(0, 255, 0)
    	dc.Fill()
    
    	dc.RotateAbout(gg.Radians(45), float64(width/2), float64(height/2))
    	dc.DrawImage(img, width/4, height/4)
    	dc.SavePNG("D:\\temp\\out.png")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    输出样例图片:
    在这里插入图片描述

    添加文字

    gg中默认字体为basicfont.Face7x13,可修改为合适的字体与大小。

    设定24号字体,且默认颜色为蓝色:

    import (
    	"github.com/fogleman/gg"
    	"github.com/golang/freetype/truetype"
    	"golang.org/x/image/font/gofont/goregular"
    )
    
    func initTextContext(w, h int) *gg.Context {
    	dc := gg.NewContext(w, h)
    	dc.DrawRectangle(0, 0, float64(w), float64(h))
    	dc.SetRGB255(245, 245, 245)
    	dc.Fill()
    
    	dc.SetRGB255(0, 0, 255)
    
    	font, err := truetype.Parse(goregular.TTF)
    	if err != nil {
    		log.Fatal(err)
    	}
    	face := truetype.NewFace(font, &truetype.Options{Size: 24})
    	dc.SetFontFace(face)
    
    	return dc
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    绘制文本

    通过DrawString(s string, x, y float64)可方便地绘制文本:

    • 默认为左对齐;
    • 坐标点(x,y)为文本框的左下角位置:所以在左上角上绘制时坐标为(0, dc.FontHeight())

    通过MeasureString可计算要绘制文本的宽、高。

    func drawText() {
    	width := 300
    	height := 300
    	dc := gg.NewContext(width, height)
    	dc.DrawRectangle(0, 0, float64(width), float64(width))
    	dc.SetRGB255(245, 245, 245)
    	dc.Fill()
    
    	dc.SetRGB255(0, 0, 255)
    
    	pos := dc.FontHeight()
    	text := "Hello World!"
    	dc.DrawString(text, 0, pos)
    
    	// set font
    	font, err := truetype.Parse(goregular.TTF)
    	if err != nil {
    		log.Fatal(err)
    	}
    	face := truetype.NewFace(font, &truetype.Options{Size: 24})
    	dc.SetFontFace(face)
    
    	pos += 50
    	dc.DrawString(text, 10, pos)
    
    	// center align
    	sW, sH := dc.MeasureString(text)
    	dc.DrawString(text, (float64(width)-sW)/2, (float64(height)-sH)/2)
    
    	// bottom
    	//dc.DrawString(text, 0, float64(height)-dc.FontHeight())
    	dc.DrawString(text, 0, float64(height))
    
    	dc.SavePNG("D:\\temp\\text.png")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    样例显示:
    在这里插入图片描述

    文本对齐

    通过计算,可设定文本的对齐方式,但太麻烦;可通过DrawStringAnchored(s string, x, y, ax, ay float64)设定文本的对齐方式(w、h为文本的宽、高):

    • 具体显示位置为:(x-wax, y+hay);
    • 左对齐为:(0, dc.FontHeight(), 0, 0)
    • 居中对齐为:(float64(width/2), float64(height/2), 0.5, 0.5)
    • 右对齐为:(float64(width), float64(height), 1, 0)
    func drawAlignText() {
    	width := 300
    	height := 300
    	dc := initTextContext(width, height)
    
    	text := "Hello World!"
    	dc.DrawStringAnchored(text, 0, dc.FontHeight(), 0, 0)
    
    	// center
    	dc.DrawStringAnchored(text, float64(width/2), float64(height/2), 0.5, 0.5)
    
    	// right
    	//dc.DrawStringAnchored(text, float64(width), float64(height)-dc.FontHeight(), 1, 1)
    	dc.DrawStringAnchored(text, float64(width), float64(height), 1, 0)
    
    	dc.SavePNG("D:\\temp\\text.png")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    样例显示:
    在这里插入图片描述

    多行显示

    当文本内容过长时,需要通过DrawStringWrapped(s string, x, y, ax, ay, width, lineSpacing float64, align Align)折行显示:

    • width为显示文本框的宽度;
    • lineSpacing为行高:多少个字体高度,实际行高为lineSpacing*dc.FontHeight()
    • align为文本在文本框中对齐方式
    func drawMultiLine() {
    	width := 300
    	height := 300
    	dc := initTextContext(width, height)
    
    	text := "Hello World! Hello World2! Hello World3!"
    	dc.DrawStringWrapped(text, float64(width/2), float64(height/2), 0.5, 0.5,
    		float64(width/2), 1.5, gg.AlignLeft)
    
    	dc.SavePNG("D:\\temp\\text.png")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    样例显示:
    在这里插入图片描述

  • 相关阅读:
    “重保季”来临,网站及业务系统拒绝“裸奔”与“带病在线”!
    Java基础类型和运算符
    【数据结构1】数据结构的基本概念
    [第五空间2019 决赛]PWN5
    如何让git命令仅针对当前目录
    目标检测如何演变:从区域提议和 Haar 级联到零样本技术
    新版HBuilderX在uni_modules创建搜索search组件
    influxdb2如何同时应用多个聚合函数
    0822(036天 线程/进程07 Lock接口、集合框架01 Iterator迭代器)
    【网络服务&数据库教程】12 NoSQL 数据库
  • 原文地址:https://blog.csdn.net/alwaysrun/article/details/126572758