はじめに
今回は、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を組み合わせることで、単純な描画だけでなく、合成モードを指定して描画することもできたので、ちょっとした表現であれば合成モードを組み合わせて出来そうだなと思いました。
 
  
 
 
 
 
 
 
 
 
  
 
 
