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))
}

  

posted @ 2024-08-14 17:08  王鹏鑫  阅读(180)  评论(0)    收藏  举报