はじめに
今回は、Goで2Dグラフィックスを描画するために、パッケージの選定と、その中から実際にggを使って描画してみました。
Golangで画像描画する方法
Golangで画像描画するために、まず初めに検討したのは x/image でした。しかし、描画機能はあるものの、図形を描画したり、塗りつぶしたりするような機能はなく、扱うのがかなり面倒そうでした。
そこで、その他のパッケージを探したところ、ggとdraw2dを見つけました。
ggは、2Dグラフィックスを簡単に描画できるパッケージで、初めから線・短形・円といった図形を描画できるほか、塗りつぶしやグラデーションをかけることもでき、とにかく簡単に図形描画できるのが特徴です。
draw2dも、ggと同じような描画機能を持っているのですが、特徴としてはベクター描画のライブラリであることです。SVGやPDFで書き出すことかできるので、ラスター画像に比べてより軽量なグラフィックを描画することができます。
この2つの中から、今回は今回はggを選択しました。理由は、今回の調査をするにあたり、golangで書かれたgenerativeartを見ていたのですが、このプロジェクトが画像描画にggを使っていたためです。
ggでの画像描画
導入
go getでggを追加します。
1 | $ go get github.com/fogleman/gg |
基本的な画像描画
まず、基本的な画像描画の流れを知るために、簡単な図形描画をやってみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package main import ( "github.com/fogleman/gg" ) func main() { dc := gg.NewContext(1000, 1000) // 背景の描画 dc.DrawRectangle(0, 0, 1000, 1000) dc.SetRGB(1, 1, 1) dc.Fill() dc.DrawCircle(400, 400, 300) dc.SetRGB(0, 0, 1) dc.Fill() dc.DrawRectangle(100, 100, 200, 200) dc.SetRGB(1, 1, 0) dc.Fill() dc.SavePNG("out.png") } |
すると、以下の様な画像が生成できます。
基本的な描画の流れとしては、描画コンテキストを生成し、描画コンテキストのメソッドを使って図形を描画していきます。また、描画に関しては、
DrawXXX
系のメソッドで何をどこに描画するかを指定し、
SetRGB
でカラー指定、
Fill()
や
Storoke()
で描画方法を指定します。
ジェネレーティブしてみた
基本的な描画方法がわかったところで、乱数を使った図形描画をやってみました。ただ描画するだけでは面白くないので、bildを使い、合成モード Addを使って描画してみます。
まず、bildを追加します。
1 | $ go get github.com/anthonynsimon/bild |
次に、コードを書いていきます。
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 | package main import ( "image" "image/png" "math/rand" "os" "github.com/anthonynsimon/bild/blend" "github.com/fogleman/gg" ) func main() { rand.Seed(0) dc := gg.NewContext(1000, 1000) dc.DrawRectangle(0, 0, 1000, 1000) dc.SetRGB(0, 0, 0) dc.Fill() img := dc.Image() img = pt1(img) save("pt1.png", img) } func pt1(base image.Image) image.Image { bg := base for i := 0; i < 100; i++ { dc := gg.NewContext(1000, 1000) dc.DrawCircle(float64(rand.Intn(1000)), float64(rand.Intn(1000)), float64(rand.Intn(100))) dc.SetRGBA(rand.Float64(), rand.Float64(), rand.Float64(), 0.8) dc.Fill() fg := dc.Image() bg = blend.Add(bg, fg) } return bg } func save(filePath string, img image.Image) { imgFile, err := os.Create(filePath) defer imgFile.Close() if err != nil { log.Println("Cannot create file:", err) } png.Encode(imgFile, img) } |
以下の様な画像が生成されました。合成モードaddを使っているため、色が重なったところは明るくなっています。
さらに、ラインを使った図形も合成してみたいと思います。こちらは一気に描画してから、先ほどの画像に合成モード difference で上に合成します。
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | package main import ( "image" "image/png" "log" "math/rand" "os" "github.com/anthonynsimon/bild/blend" "github.com/fogleman/gg" ) func main() { rand.Seed(0) dc := gg.NewContext(1000, 1000) dc.DrawRectangle(0, 0, 1000, 1000) dc.SetRGB(0, 0, 0) dc.Fill() img := dc.Image() img = pt1(img) img = pt2(img) save("pt2.png", img) } func sample1() { dc := gg.NewContext(1000, 1000) // 背景の描画 dc.DrawRectangle(0, 0, 1000, 1000) dc.SetRGB(1, 1, 1) dc.Fill() dc.DrawCircle(400, 400, 300) dc.SetRGB(0, 0, 1) dc.Fill() dc.DrawRectangle(100, 100, 200, 200) dc.SetRGB(1, 1, 0) dc.Fill() dc.SavePNG("out.png") } func pt1(base image.Image) image.Image { bg := base for i := 0; i < 100; i++ { dc := gg.NewContext(1000, 1000) dc.DrawCircle(float64(rand.Intn(1000)), float64(rand.Intn(1000)), float64(rand.Intn(100))) dc.SetRGBA(rand.Float64(), rand.Float64(), rand.Float64(), 0.8) dc.Fill() //dc.Stroke() fg := dc.Image() bg = blend.Add(bg, fg) } return bg } func pt2(base image.Image) image.Image { dc := gg.NewContext(1000, 1000) for i := 0; i <= 1000; i += 25 { dc.DrawLine(0, 0, float64(i), 1000) dc.SetLineWidth(5) dc.SetRGB(1, 1, 1) dc.Stroke() dc.DrawLine(0, 1000, float64(i), float64(i)) dc.SetLineWidth(5) dc.SetRGB(1, 1, 1) dc.Stroke() } return blend.Difference(base, dc.Image()) } func save(filePath string, img image.Image) { imgFile, err := os.Create(filePath) defer imgFile.Close() if err != nil { log.Println("Cannot create file:", err) } png.Encode(imgFile, img) } |
以下の様な画像が生成されます。
さいごに
ggとbildを使って2Dグラフィックスの描画をやってみました。bildを組み合わせることで、単純な描画だけでなく、合成モードを指定して描画することもできたので、ちょっとした表現であれば合成モードを組み合わせて出来そうだなと思いました。