gg 库的基本使用
安装
go get -u github.com/fogleman/gg
这是它例子的大概效果图,可以看到,能实现的图形效果还是挺多的。

圆形图生成事例:
package main
import "github.com/fogleman/gg"
func main() {
dc := gg.NewContext(1000, 1000)
dc.DrawCircle(500, 500, 400)
dc.SetRGB(0, 0, 0)
dc.Fill()
dc.SavePNG("out.png")
}
调整生成图片的大小
dc := gg.NewContext(1000, 1000)
这是先初始化一个 Context 对象,使用库之前都需要先初始化一个 Context 对象,后面的两个 1000,分别对应着最终生成图片的宽度和高度。我们可以修改一下高度为 500 看看效果。
dc := gg.NewContext(1000, 500)

调整圆形参数
dc.DrawCircle(500, 500, 400)
这里是画一个圆心位置在 (500,500),半径为 400 的圆形。需要说明的一点是,这里的坐标轴是以左上角为原点的,水平向右是横轴正方向,竖直向下是纵轴正方向,所以(500,500)正是中心位置。我们可以调整一下参数值来看下效果。
dc := gg.NewContext(1000, 1000) dc.DrawCircle(300, 300, 400)

调整颜色
var rbgConverter = func(r []float64) []float64 {
result := make([]float64, 0)
for i := 0; i < len(r); i++ {
result = append(result, r[i] / 255)
}
return result
}
func main() {
dc := gg.NewContext(1000, 1000)
dc.DrawCircle(500, 500, 400)
rgb := []float64{255,222,173}
newRgb := rbgConverter(rgb)
dc.SetRGB(newRgb[0], newRgb[1], newRgb[2])
dc.Fill()
dc.SavePNG("out.png")
}

保存图片
dc.SavePNG("out.png")
// SavePNG encodes the image as a PNG and writes it to disk.
func (dc *Context) SavePNG(path string) error {
return SavePNG(path, dc.im)
}
func SavePNG(path string, im image.Image) error {
file, err := os.Create(path)
if err != nil {
return err
}
defer file.Close()
return png.Encode(file, im)
}
加载图片
加载图片有三个方法可以实现,LoadPNG() 用来加载 PNG 格式的图片,LoadJPG() 用来加载 JPG 格式的图片,如果不清楚该用什么方法,可以直接使用 LoadImage() 方法来加载图片。
加载图片后,得到的是一个 image.Image 对象,可以通过该对象来获取图片的一些基本信息,如:图片的长和宽、图片某一点的颜色RGB值。
加载图片之后,可以使用 DrawImage() 方法来将图片绘制出来,以便用于后续操作。
im, err := gg.LoadImage("/Users/bytedance/Desktop/test.jpg")
if err != nil {
panic(err)
}
w := im.Bounds().Size().X
h := im.Bounds().Size().Y
dc := gg.NewContext(h, w)
dc.DrawImage(im, 0, 0)
裁剪
有一个经常使用到的方法叫 Clip() ,该方法可以将图像进行裁剪,裁剪的形状取决于之前画的图形。
下面来举个例子,这是我们要裁剪的图,我们用一个圆形来对它进行裁剪。

func main() {
// 加载图片,这里路径换成自己的
im, err := gg.LoadImage("test.jpg")
if err != nil {
panic(err)
}
// 获取图片的宽度和高度
w := im.Bounds().Size().X
h := im.Bounds().Size().Y
dc := gg.NewContext(h, w)
// 取宽度和高度的最小值作为直径
radius := math.Min(float64(w), float64(h)) / 2
// 画圆形
dc.DrawCircle(float64(w/2), float64(h/2), radius)
// 对画布进行裁剪
dc.Clip()
// 加载图片
dc.DrawImage(im, 0, 0)
dc.SavePNG("out.png")
}
实际项目中使用
package test
import (
"encoding/binary"
"fmt"
"game_common/app/actors/map_engine/common/file"
"game_common/app/actors/map_engine/geo/static_resource"
"game_common/app/common_model/common_map"
"game_common/confs/gd"
"game_logic/app/models/map/map_common/domain"
"game_logic/app/models/map/map_main"
"github.com/fogleman/gg"
"io/ioutil"
"os"
"su/goslib/core/logger"
"su/goslib/gosconf"
"su/goslib/modules/config_data"
"su/goslib/modules/database"
"testing"
)
var rbgConverter = func(r []float64) []float64 {
result := make([]float64, 0)
for i := 0; i < len(r); i++ {
result = append(result, r[i]/255)
}
return result
}
var rgbMap = map[int32][]float64{
0: {248, 248, 255}, // 白色
1: {255, 222, 173}, // 橙色
2: {202, 235, 216}, // 天蓝灰
3: {255, 0, 0}, // 红色
4: {255, 255, 0}, // 黄色
5: {255, 105, 180}, // 粉色
6: {138, 43, 226}, // 紫色
7: {100, 149, 237}, // 蓝色
8: {50, 205, 50}, // 绿色
9: {255, 105, 180}, // 粉色
}
func TestX(t *testing.T) {
fmt.Printf("%b", 0xff)
}
func setCampPos(dc *gg.Context, x, y, campId int32) {
rgb, ok := rgbMap[campId]
if !ok {
logger.ERR("campId is nil: ", campId)
return
}
newRgb := rbgConverter(rgb)
dc.SetRGB(newRgb[0], newRgb[1], newRgb[2])
dc.DrawPoint(float64(x), float64(y), 1)
dc.Fill()
}
var terrainData *static_resource.Data
var baseMapData *static_resource.Data
var monsterData *static_resource.Data
var mapLen = int32(1500)
func load(path string) ([]byte, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer func() {
_ = f.Close()
}()
return ioutil.ReadAll(f)
}
func loadStaticData() {
data, err := load("../confs/season_basemap.bytes")
if err != nil {
logger.ERR("err: ", err)
return
}
baseMapData = static_resource.NewData(mapLen*mapLen, static_resource.BaseFile)
for k, v := range data {
x := int32(k) / mapLen
y := int32(k) % mapLen
dataId := v & 0xff
if dataId == 0 {
continue
}
idx := static_resource.XYToIdx(mapLen, x, y, 1)
baseMapData.SetUint8(dataId, idx)
}
data, err = load("../confs/season_map.bytes")
if err != nil {
logger.ERR("err: ", err)
return
}
gConf := gd.GetGlobal()
terrainData = static_resource.NewData(mapLen*mapLen, static_resource.TerrainFile)
//dataSF := static_resource.NewData(mapLen*mapLen*2, resource.SurfaceFeaturesFile)
bytes := make([]byte, 2)
for i := 0; i < len(data); i += 2 {
x := int32(i/2) / mapLen
y := int32(i/2) % mapLen
bytes[0], bytes[1] = data[i], data[i+1]
id := binary.BigEndian.Uint16(bytes)
if id == 0 {
continue
}
cid := int32(id)
if cid == 0 {
continue
}
conf := gd.SeasonPathMapIns.GetItem(cid)
if conf == nil || conf.AttrType == 0 {
continue
}
if conf.AttrType == gConf.MapTypeTerrain {
//存储地貌
idx := static_resource.XYToIdx(mapLen, x, y, 1)
terrainData.SetUint8(1, idx)
} else {
//存储地物
//idx := resource.XYToIdx(mapLen, x, y, 2)
//dataSF.SetUint16B(idx, bytes)
}
}
data, err = load("../confs/season_monster.bytes")
if err != nil {
logger.ERR("err: ", err)
return
}
monsterData = static_resource.NewData(mapLen*mapLen, static_resource.MonsterFile)
for k, v := range data {
// 地编数据为0表示不刷怪
if v == 0 {
continue
}
// 设置可刷新山寨怪的标记
x := int32(k) / mapLen
y := int32(k) % mapLen
idx := mapLen*y + x
monsterData.SetUint8(1, idx)
}
}
func TestCreatePng(t *testing.T) {
//加载配置
if err := gosconf.ParseAndLoad(); err != nil {
panic(err)
}
// 启动MySQL
database.StartMySQL()
// 启动Redis
database.StartRedis()
// 启动配置服务
if err := new(config_data.ConfigData).Start(gd.LoadConfigs); err != nil {
panic(err)
}
sceneId := "S:12:100100000"
domainData := file.NewFile4Byte(mapLen, sceneId, "domainData")
if err := domainData.OnLoad(); err != nil {
logger.ERR("err: ", err)
return
}
loadStaticData()
dc := gg.NewContext(1500, 1500)
dc.SetRGB(0, 0, 0)
dc.Clear()
getData(domainData, 1127, 1327)
getData(domainData, 1199, 1410)
getData(domainData, 1227, 1414)
getData(domainData, 1364, 1471)
getData(domainData, 989, 1391)
getData(domainData, 615, 268)
getData(domainData, 686, 372)
getData(domainData, 699, 364)
getData(domainData, 717, 369)
posId := common_map.PosNum(615, 268)
zone := map_main.PosNumToZone(mapLen, posId)
logger.DEBUG("zone: ", zone, " posId: ", posId)
//getData(domainData, 845, 786)
//for x := int32(0); x < mapLen; x++ {
// for y := int32(0); y < mapLen; y++ {
// d := domainData.GetDataUint32(x, y)
// campD := domain.GetCampD(d)
// //status := domain.GetStatus(d)
// if campD != 0 {
// setCampPos(dc, x, y, campD)
// // logger.DEBUG("x: ", x, " y: ", y, " campD: ", campD, " status: ", status)
// }
// idx := static_resource.XYToIdx(mapLen, x, y, 1)
// if terrainData.GetUint8(idx) != 0 {
// setCampPos(dc, x, y, 0)
// //logger.INFO("x: ", x, " y: ", y, " block!")
// }
// }
//}
//dc.SavePNG("test.png")
}
func getData(domainData *file.File4Byte, x, y int32) {
d := domainData.GetDataUint32(x, y)
campD := domain.GetCampD(d)
status := domain.GetStatus(d)
idx := static_resource.XYToIdx(mapLen, x, y, 1)
countyId := 100140000 + int32(baseMapData.GetUint8(idx))
conf := gd.SeasonCountyIns.GetItem(countyId)
if conf != nil {
logger.DEBUG("conf: ", conf.StateID)
}
logger.DEBUG("x: ", x, " y: ", y, " campD: ", campD, " status: ", status, " baseMap: ", baseMapData.GetUint8(idx), " "+
" terrain: ", terrainData.GetUint8(idx) != 0, " monster: ", monsterData.GetUint8(idx))
}

浙公网安备 33010602011771号