Golang image标准库的使用

简介

go中的image和image/color包中利用颜色color.Color和色彩模式color.Model来描述图片的颜色,image.Point和image.Rectangle则用来表示图像的2D几何形状. image.Image则把这两个概念组合在一起来表示一张图片.

color.Color是一个接口,定义如下

1
2
3
4
type Color interface{
//该方法返回rgba值,,转化可能是有损的
RGBA() (r, g, b, a uint32)
}

color.Model接口,可以把自身的模式转换为其他模式,转化可能是有损的

1
2
3
type Model interface {
Convert(c Color) Color
}

image.Point是在以向右为x轴正方向,向下为y轴正方向的坐标系中的点

1
2
3
type Point struct {
X, Y int
}

image.Rectangle长方形则是由两个Point组成,两个点分别表示长方形左上角和右下角在上述坐标系中的位置

1
2
3
type Rectangle struct {
Min, Max Point
}

image中包含了长方形中的每个像素的颜色和模式,其定义很好的解释这一点

1
2
3
4
5
6
7
8
type Image interface {
// 色彩模式.
ColorModel() color.Model
//长方形
Bounds() Rectangle
//xy 处的颜色
At(x, y int) color.Color
}

常见的色彩模式有RGBA,NRGBA,CMYK等等等.

应用

在游戏开发中,经常会使用合图,来减少图片数量,提升加载速度.bmfont文件其实也是利用了合图,只是配置文件中除了包含子图片位置信息之外,还包含了每个子图片对应的字符以及其他一些信息.

在开发过程中有时需要把合图切开,只需要从配置文件中读取合图中的子图片信息,然后利用一些工具(比如python中的pillow)就能将合图分割成一张张小图

下面利用image库来对bmfont进行切图

首先bmfont字体通常包含一个fnt文件和一张或多张png图片,fnt文件通常格式如下

1
2
3
4
5
6
7
8
info face="Arial" size=-32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=0,1,1,0 spacing=1,1 outline=0
common lineHeight=46 base=29 scaleW=200 scaleH=200 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
page id=0 file="font.png"
chars count=19
char id=44 x=50 y=94 width=14 height=46 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15
char id=46 x=65 y=94 width=14 height=46 xoffset=0 yoffset=0 xadvance=9 page=0 chnl=15
char id=48 x=165 y=0 width=24 height=46 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15
char id=49 x=0 y=47 width=24 height=46 xoffset=0 yoffset=0 xadvance=19 page=0 chnl=15

其中对切图有用的信息只有file指定的png信息,以及指定字符的行中的x,y,width,height,其他字段的含义在此不再赘述

page 行表示该bmfont文件的中包含的png图片数量,file问png文件名,id为png文件索引

x,y 表示子图的左上角的坐标,width和height表示图片的宽高,结合上述的image.Rectangle,一张子图的长方形表示就是

1
2
3
4
image.Rectangle{
Min image.Point{X:x,Y:y},
Max image.Point{X:x+width,Y:y+height},
}

已经知道子图片在合图中的位置,就容易把它从中取出来,先进行一些定义

1
2
3
4
5
6
7
8
9
10
11
12
13
//Font 每个字符信息
type Font struct {
Id int //字符id
Rect image.Rectangle //子图的长方形信息
PageId int //对应的png索引
}

type FontInfo struct {
Pages map[int]string //所有png的集合,key为pageId,value为png名称
Fonts []Font //包含的所有字符信息
Dir string //bmfont文件路径
Name string//bmfont文件名称,切的子图放在该名称的文件夹中
}

现在开始处理bmfont的配置文件,读取文件后,按行处理,然后利用正则表达式匹配x,y,width,height,id和pageid,部分代码如下

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
36
37
38
39
40
41
42
43
44
45
46
//解析fnt文件
func parseContent(lines []string) (fontInfo FontInfo, err error) {
fontInfo = FontInfo{
Pages: map[int]string{},
Fonts: make([]Font, 0),
}
for _, line := range lines {
if Has(line, "page") && Has(line, "file") {
re := regexp.MustCompile(`file="(.*?)"`)
s := re.FindString(line)
fileName := Replace(s, "file=", "")
fileName = Replace(fileName, `"`, "")//png文件名

re = regexp.MustCompile(`pageid=\d+`)
s = re.FindString(line)
s = Replace(s, "pageid=", "")
pageId, e := strconv.Atoi(s)//png文件索引
if err != nil {
log.Println("pageId to int error")
err = e
return
}
fontInfo.Pages[pageId] = fileName
}
if Has(line, "charid") {//对每行的空格都进行了替换处理,所以char id 变成 charid
font := Font{}
re := regexp.MustCompile(`x=\d+`)
s := re.FindString(line)
x := Replace(s, "x=", "")

X, e := strconv.Atoi(x)
if err != nil {
log.Println("X to int error")
err = e
return
}
font.Rect.Min.X = X
//...
//...
//...

fontInfo.Fonts = append(fontInfo.Fonts, font)
}
}
return
}

下面开始切图,切图使用SubImage方法,该方法原型如下,输入一个长方形,返回该长方形大小和位置的的图片

1
type SubImage func(r Rectangle) Image

读取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
   pngFile := path.Join(fonts.Dir, fonts.Pages[font.PageId])

file, err := os.Open(pngFile)
//错误处理忽略
img, err := png.Decode(file)
var newPng image.Image
//下面要对图片类型进行判断才能切图
switch img.(type) {
case *image.RGBA:
newPng = img.(*image.RGBA).SubImage(font.Rect)
//...
//...
//...
//...
}
pngDir := path.Join(fonts.Dir, fonts.Name)
_, err = os.Stat(pngDir)
if os.IsNotExist(err) {
os.MkdirAll(pngDir, 0777)
}
outFileName := path.Join(pngDir, string(rune(font.Id))+".png")//文件名和路径
outFile, err := os.OpenFile(outFileName, os.O_CREATE, 0777)
defer outFile.Close()
err = png.Encode(outFile, newPng)//写入文件

以上就是使用go的image库切图的应用