はじめに
こんにちは、Go界のスタンリー・キューブリックです。このエントリはGo Advent Calendar 2013の22日目のエントリです。
2013年はGoカンファレンスも2回開催できましたし、私としてもかなりGoの広まりを実感出来た1年でした。来年はもっと多くの方にGoを使ってもらえたらと思いつつ、さらにプロダクション事例などを期待しています。
さて、Goなんですが、ネイティブにビルドされるとか、並列化が簡単にできるとか、そういうところがよく取り上げられますが、個人的にはimageパッケージを標準で持っているのが大きいと思っています。ということで、今回はimageパッケージを使ってアニメーションGIFを作ってみたいと思います。
imageパッケージとは
名前のとおり画像データを簡単に扱えるようにしたパッケージで、Go1.2の時点ではGIF、JPEG、PNGなどを簡単に読み込み・書き出し出来るようになっています。今回はPNG画像を読み込んで、アニメーションGIFにするということをやってみたいと思います。
まずこの可愛らしいGopherちゃんの画像を用意します。今回はサンプルということで、正方形の画像を用意しました。
クリスマスなので、このGopherちゃんがぐるぐると回転するアニメーションGIFができたらカワイイと思いませんか?思いますね!?じゃあ実際にぐるぐる回してみましょう!!
簡単に使い方解説
画像データの読み込み・書き込み
Goっぽく、まずはファイルを読み込んで(io.Readerを用意)して、そのあとDecoderに渡す、という流れです。
// Open source PNG file. file, err := os.Open("image/gopher.png") if err != nil { panic(err) } defer file.Close() data, err := png.Decode(file) if err != nil { panic(err) }
逆に書き込みは画像データと書き込み先のio.Writerを用意しておいて、Encodeに渡すという流れ。
// Dump image data into file. file, err = os.Create("image/rotate-gopher.gif") if err != nil { panic(err) } defer file.Close() err = gif.EncodeAll(file, &dst) if err != nil { panic(err) }
画像データの操作
読み込みと書き出しがわかったので、肝心の画像データの操作についてですが、扱う画像形式によって必要となる画像データ型が異なります。(image.Image、image.Palettedなど)ですが基本的には、領域を用意し(image.Rectangle)、色データを操作する(xxx.Setメソッド)という流れになります。
original := image.NewPaletted(r, palette.WebSafe) for x := r.Min.X; x < r.Max.X; x++ { for y := r.Min.Y; y < r.Max.Y; y++ { original.Set(x, y, data.At(x, y)) } } dst.Image = append(dst.Image, original)
たったこれだけで画像が扱えるんです。簡単!!ただし、現状では画像の入出力系の基本的な操作しか出来ないので、アフィン変換などは自分で実装する必要があります。では実際に作ってみた画像を見てください!
回っていますね!
これであなたもローリングGopherをいつでも作れます!今年のクリスマスはぜひローリングGopherちゃんと戯れてください!次はyanolabさんです。
サンプルコード全体
package main import ( "image" "image/color/palette" "image/gif" "image/png" "log" "os" ) func main() { // Open source PNG file. file, err := os.Open("image/gopher.png") if err != nil { panic(err) } defer file.Close() data, err := png.Decode(file) if err != nil { panic(err) } // Define destination boundary. Expecting original image is square. r := data.Bounds() // Prepare distination image buffer. dst := gif.GIF{ Image: []*image.Paletted{}, } // Rotate original image and store them into destination. original := image.NewPaletted(r, palette.WebSafe) for x := r.Min.X; x < r.Max.X; x++ { for y := r.Min.Y; y < r.Max.Y; y++ { original.Set(x, y, data.At(x, y)) } } dst.Image = append(dst.Image, original) clockwise := image.NewPaletted(r, palette.WebSafe) for x := r.Min.X; x < r.Max.X; x++ { for y := r.Min.Y; y < r.Max.Y; y++ { clockwise.Set(x, y, data.At(-y+r.Max.Y, x)) } } dst.Image = append(dst.Image, clockwise) upsidedown := image.NewPaletted(r, palette.WebSafe) for x := r.Min.X; x < r.Max.X; x++ { for y := r.Min.Y; y < r.Max.Y; y++ { upsidedown.Set(x, y, data.At(-y+r.Max.Y, -x+r.Max.X)) } } dst.Image = append(dst.Image, upsidedown) counterclockwise := image.NewPaletted(r, palette.WebSafe) for x := r.Min.X; x < r.Max.X; x++ { for y := r.Min.Y; y < r.Max.Y; y++ { counterclockwise.Set(x, y, data.At(x, -y+r.Max.X)) } } dst.Image = append(dst.Image, counterclockwise) // Post process dst.Delay = make([]int, len(dst.Image)) dst.LoopCount = 100 // Dump image data into file. file, err = os.Create("image/rotate-gopher.gif") if err != nil { panic(err) } defer file.Close() err = gif.EncodeAll(file, &dst) if err != nil { panic(err) } log.Println("wrote out rotate-gopher.gif") }