読者です 読者をやめる 読者になる 読者になる

今川館

都内勤務の地味OLです

Goのimageモジュールを使って絵を描く

この記事のソースコードこちら

imageモジュールを使った描画の記事いろいろ

今月のGoの勉強の締めくくりに、画像ライブラリを使って絵を描いてみたくなった。
とはいえ、わたしは今までPythonのPILを使って画像のペーストやリサイズ程度のことしか経験がない。
一から絵を描く場合、どうしたら良いかまったく知見が無いので、他の人の記事を参考にやり方を調べた。

Go 言語でソースコードから画像生成する - てっく煮ブログ

「プログラムでシダを描画する」をGoで描画する - Qiita

これらの記事がどういうロジックで正確な画像を作っているのか恥ずかしながらよく分からなかったのだが、とりあえずimage.Rectで四角いパレットを作って、その中の座標にひたすらcolor.RGBAの色成分を埋めていけば絵が描けそうなことは分かった。

imageのRGBAとcolorのRGBA

Goのライブラリにはimage.RGBAとimage/color.RGBAの同じ名前の2種類の構造体が存在する。
color.RGBAの方が赤・緑・青の三原色を表す色成分を、image.RGBAの方がRGBAで表すインメモリの画像オブジェクトに該当するらしい。

imageのRGBAはimage.NewRGBA()で作れる。

円を描く

円を描くためには

if ある座標が円の中に入っていれば {
	円の色を指定
} else {
	背景色を指定
}

こうすれば良いことまで理解した。

問題は、「円の中に座標が入っている」ことを確かめる方法なのだが、以下記事にやり方が書いてあったので拝借。

任意の点(x, y)が円内に含まれているかどうか

冒頭の「てっく煮ブログ」さんのサンプルコードも参考に円を表す構造体のメソッドを定義した。(以下)

type Circle struct {
	X, Y, R float64
}

func (c *Circle) Inside(x, y int) bool {
	// sqrt((x-a)^2 + (y-b)^2) は二点(x, y), (a, b)間の距離
	// これが(x, y)を中心とする円の半径r以内の長さであれば円の内側

	xx, yy := c.X-float64(x), c.Y-float64(y)
	return math.Sqrt(xx*xx+yy*yy) <= c.R
}

サンプルコード

f:id:imagawa_yakata:20161221184327p:plain

以下の通り、DrawingHandlerを定義して、/newyearというPathに紐付けたので、localhost:8080/newyearを問い合わせると上記の画像が返却される。

[lib/drawing/main.go]

package drawing

import (
	"image"
	"image/color"
	"image/png"
	"math"
	"net/http"
)

type Circle struct {
	X, Y, R float64
}

func (c *Circle) Inside(x, y int) bool {
	// sqrt((x-a)^2 + (y-b)^2) は二点(x, y), (a, b)間の距離
	// これが(x, y)を中心とする円の半径r以内の長さであれば円の内側

	xx, yy := c.X-float64(x), c.Y-float64(y)
	return math.Sqrt(xx*xx+yy*yy) <= c.R
}

func NewImage() *image.RGBA {
	var w, h int = 280, 240
	m := image.NewRGBA(image.Rect(0, 0, w, h))

	large := Circle{140, 240, 80}
	middle := Circle{140, 180, 50}
	small := Circle{140, 120, 20}

	white := color.RGBA{
		255,
		255,
		255,
		255,
	}
	another := color.RGBA{
		170,
		179,
		0,
		255,
	}
	orange := color.RGBA{
		255,
		102,
		0,
		255,
	}

	for x := 0; x < w; x++ {
		for y := 0; y < h; y++ {
			var c color.RGBA
			if small.Inside(x, y) {
				// ちっちゃい円の中にある座標はオレンジ色(橙の絵)
				c = orange
			} else if large.Inside(x, y) || middle.Inside(x, y) {
				// 大きい円、または中くらいの円の中にある座標は白(お餅の絵)
				c = white
			} else {
				// それ以外は背景とする
				c = another
			}
			m.Set(x, y, c)
		}
	}
	return m
}

func DrawingHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "image/png")
	img := NewImage()
	png.Encode(w, img)
}

[main.go]

// import "github.com/oyakata/kanjo/lib/drawing"

func init() {
	// 一部抜粋
	http.HandleFunc("/newyear", drawing.DrawingHandler)

}

鏡餅の絵に見えると思うんだけど、どうかなぁ・・

良いお年を。