カテゴリー: Tech

Golang で図形描画してみた

はじめに

今回は、Goで2Dグラフィックスを描画するために、パッケージの選定と、その中から実際にggを使って描画してみました。

Golangで画像描画する方法

Golangで画像描画するために、まず初めに検討したのは x/image でした。しかし、描画機能はあるものの、図形を描画したり、塗りつぶしたりするような機能はなく、扱うのがかなり面倒そうでした。
そこで、その他のパッケージを探したところ、ggdraw2dを見つけました。

ggは、2Dグラフィックスを簡単に描画できるパッケージで、初めから線・短形・円といった図形を描画できるほか、塗りつぶしやグラデーションをかけることもでき、とにかく簡単に図形描画できるのが特徴です。
draw2dも、ggと同じような描画機能を持っているのですが、特徴としてはベクター描画のライブラリであることです。SVGやPDFで書き出すことかできるので、ラスター画像に比べてより軽量なグラフィックを描画することができます。

この2つの中から、今回は今回はggを選択しました。理由は、今回の調査をするにあたり、golangで書かれたgenerativeartを見ていたのですが、このプロジェクトが画像描画にggを使っていたためです。

ggでの画像描画

導入

go getでggを追加します。

$ go get github.com/fogleman/gg

基本的な画像描画

まず、基本的な画像描画の流れを知るために、簡単な図形描画をやってみました。

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を追加します。

$ go get github.com/anthonynsimon/bild

次に、コードを書いていきます。

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 で上に合成します。

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

おすすめ書籍

カイザー

シェア
執筆者:
カイザー

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

1か月 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前