画像作成

とある授業で画像変換機能実装の課題が出ました。 何から手をつければ良いかよくわからなかったが、とりあえず image パッケージなるものが golang.org を眺めていたところ見つかったので、 手始めに何か触ってみようと思った。

個人的には、いきなり 画像変換 Go とかでググるより 一次情報にアクセスした方がモテるという話を聞いたので、 小一時間に渡り公式ドキュメントというなの樹海(僕にとって)をさまようことになる…

だらだらとパッケージのドキュメントをみていると、 image/png というパッケージの中に、Encode なる関数が見つかった。 なんかこいつを使えば、簡単な画像とか生成できるんじゃね? と思った。 Encode関数はこんな感じだ。

type.Encode

func Encode(w io.Writer, m image.Image) error

第一引数に io.Writer がきているので、どこかしらに画像を書き込める匂いがプンプンしている。 これだ! と思った はいいものの、第二引数の image.Image というものがわからん。

リンクを飛んでみると、どうやら 画像情報を保持しているインターフェースのようだ。

type Image interface {
        ColorModel() color.Model
        Bounds()     Rectangle
        At(x, y int) color.Color
}

よくわからん。 だが golang.org のサイトはありがたいことに、多くの関数に対して 使用例exampleを用意してくれている。 func Encode の使用例は以下。

// Create a colored image of the given width and height.
	img := image.NewNRGBA(image.Rect(0, 0, width, height))

	for y := 0; y < height; y++ {
		for x := 0; x < width; x++ {
			img.Set(x, y, color.NRGBA{
				R: uint8((x + y) & 255),
				G: uint8((x + y) << 1 & 255),
				B: uint8((x + y) << 2 & 255),
				A: 255,
			})
		}
	}
	
	f, err := os.Create("image.png")
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	if err := png.Encode(f, img); err != nil {
    		f.Close()
    		log.Fatal(err)
    	}

この例をみると、 png.Encode(f, img) のところで io.Writer と img を渡している。 この img が上記であげた image.Image インターフェースに当たるわけだが、 こいつは

img := image.NewNRGBA(image.Rect(0, 0, width, height))

というように NewNRGBAという機能で生成されている。 (New… という名前が完全に生成っぽい)

この関数は 返り値に RGBA 型を当てている。 RGBA型ってなんだ?って思ってリンクを辿ってみたところ、下記のようなメンバ(表現として合っていたかな?)を保持しているようだ。

type RGBA struct {
        // Pix holds the image's pixels, in R, G, B, A order. The pixel at
        // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4].
        Pix []uint8
        // Stride is the Pix stride (in bytes) between vertically adjacent pixels.
        Stride int
        // Rect is the image's bounds.
        Rect Rectangle
}

よくわからんが、 png.Encode 関数の引数として渡せれる img は、Imageインターフェースを満たしている必要があり、 image.NewNRGBA関数で作成される RGBA型 はこのImageインターフェースを満たしている、ということのようだ。

png を作成

これ前の内容をずらずらとコードにまとめる。 以下のコードを実行すると、真っ黒なpngが生成される。

package main

import (
	"image"
	"image/color"
	"image/png"
	"log"
	"os"
)

func main() {

	min := image.Point{X: 10, Y: 10}
	max := image.Point{X: 110, Y: 110}
	r := image.Rectangle{Min: min, Max: max}

	// NRGBA を作成
	n := image.NewNRGBA(r)

	// 描画
	for h := r.Min.Y; h < r.Max.Y; h++ {
		for v := r.Min.X; v < r.Max.X; v++ {
			n.Set(v, h, color.RGBA{
				R: uint8(0),
				G: uint8(0),
				B: uint8(0),
				A: uint8(255),
			})
		}
	}

	// io.Writer 作成
	f, err := os.Create("img.png")
	if err != nil {
		log.Fatal(err)
	}

	defer f.Close()

	// エンコード
	if err := png.Encode(f, n); err != nil {
		log.Fatal(err)
	}

}