Re:ゼロから始めないAPNGエンコーダ

## はじめに
先日、[ChromiumでAPNG(Animated PNG)がサポートされた](https://chromium.googlesource.com/chromium/src/+/7d2b8c45afc9c0230410011293cc2e1dbb8943a7)というニュースを聞いて、Golang製のAPNGエンコーダ**goapng**を作って見ました。
ただ、現状APNGが対応しているブラウザはFirefoxとSafariくらいであまり使い道がないんですよね・・・（~~Chromeは[Extentionを入れる](https://chrome.google.com/webstore/detail/apng/ehkepjiconegkhpodgoaeamnpckdbblp)か[2017年の6月まで待てば](http://qiita.com/otmb/items/ea86567e7e9338f07b1c#20170318-%E7%8F%BE%E5%9C%A8%E3%81%AE%E7%8A%B6%E6%B3%81)と動きます。~~Chromeはバージョン59になりAPNGに対応しました。）。24bit+α値のアニメーションも作れるし、今後対応するブラウザが増えていってほしいですね。
また、ブラウザじゃないですが[LINEスタンプはAPNGに対応しているらしい](https://creator.line.me/ja/guideline/animationsticker/)ので、スタンプ作成自動化なんかに使ってみてもいいかもしれませんね。

goapngで生成した動くGopherはこんな感じ。 
（画像は「[本物の golang を... 本物の Gopher を、お見せしますよ。 - Qiita](http://qiita.com/mattn/items/b7889e3c036b408ae8bd)」からお借りしました。）

![animated_gopher.png](https://qiita-image-store.s3.amazonaws.com/0/72064/5b8791a7-736f-9d03-1073-408c672b4b5e.png)

_コイツ・・・動くぞ_（動かないかも）

## インストール
go getで一発。そうGolangならね

``$ go get github.com/cia-rana/goapng``

## 使い方
使い方はいたってシンプル 
goapngをimportして、goapng.APNGに画像データと遅延情報(delay)をつっこみエンコードするだけ！ 
インターフェースは[アニメーションgifの作り方](http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/)と同じにしたので、APNGとアニメーションgifと出力を切り替えるのも簡単です。

```go
package main

import (
	"github.com/cia-rana/goapng"
	"image/png"
	"os"
	"fmt"
)

func main() {
	inPaths := []string{
		"res/gopher01.png",
		"res/gopher02.png",
		"res/gopher03.png",
	}
	outPath := "res/animated_gopher.png"
	
	// Assemble output image.
	outApng := &goapng.APNG{}
	for _, inPath := range inPaths {
		// Read image file.
		f, err := os.Open(inPath)
		if err != nil {
			fmt.Println(err)
			f.Close()
			return
		}
		inPng, err := png.Decode(f)
		if err != nil {
			fmt.Println(err)
			f.Close()
			return
		}
		f.Close()
		
		// Append a frame(type: *image.Image). First frame used as the default image.
		outApng.Image = append(outApng.Image, &inPng)

		// Append a delay time(type: uint32) per frame in 10 milliseconds.
		// If it is 0, the decoder renders the next frame as quickly as possible.
		outApng.Delay = append(outApng.Delay, 0)
	}

	// Encode images to APNG image.
	f, err := os.Create(outPath)
	if err != nil {
		fmt.Println(err)
		f.Close()
		
		return
	}
	if err = goapng.EncodeAll(f, outApng); err != nil {
		fmt.Println(err)
		f.Close()
		os.Remove(outPath)
		return
	}
	f.Close()
}
```

## エンコーダの簡単な概要
APNG仕様はただのPNG仕様の拡張なので（補助チャンクが3つ増えただけ）、めんどうくさい圧縮処理はimage/pngに任せて、image/pngを使って吐いたバイナリに補助チャンクを挿入するといった処理をしています。APNGの詳しい概要は[こちら](https://wiki.mozilla.org/APNG_Specification)をご覧ください。

## 対応画像
しっかりテストはしていませんが、画像内部でパレットを使っている場合を除き、image.Imageに変換できればどんな画像でもいけるはずです。また、APNGの仕様では、2フレーム目以降の画像の幅・高さは、1フレーム目の画像のそれ以内に収まっていればどんな大きさでも構わないことになっています。

## ソース
[MITで公開](https://github.com/cia-rana/goapng)しています。煮るなり焼くなり好きにしてください。

## その他APNG関連ツール
[ココ](http://littlesvr.ca/apng/)にCLIやらGUIやらいっぱい載ってます。特にapngasmはコードがGitHubで公開されているので、goapng作成の際は参考にさせてもらいました。

## 参考
- [APNGの構造メモ - Gist](https://gist.github.com/cia-rana/71275c7db98c2887e8ab0fad365add4a)
- [Animated PNG graphics - MDN](https://wiki.mozilla.org/APNG_Specification)
- [PNG イメージを自力でパースしてみる ～1/6 予備知識編～ - 自由研究ノート(仮)とかいう名前の備忘録](http://darkcrowcorvus.hatenablog.jp/entry/2016/09/23/195644)
- [PNG を自力で読んで表示しよう その１ - 雑念日記 主に技術的なことをつらとらと（書ければいいな）。](http://hoshi-sano.hatenablog.com/entry/2013/08/18/112550)
- [PNG (Portable Network Graphics) Specification, Version 1.2 ~ 12. 付録：原理付録：原理](http://www.awm.jp/~yoya/cache/tech.millto.net/~pngnews/kndh/PngSpec1.2/PNG-Rationale.html)
- [image/png - GitHub](https://github.com/golang/go/tree/master/src/image/png)
- [image/gif - GitHub](https://github.com/golang/go/tree/master/src/image/gif)
- [LINE CREATORS MARKET 制作ガイドライン](https://creator.line.me/ja/guideline/animationsticker/)
- [PNG軽量化の減色と圧縮について - Gree Enginer](http://labs.gree.jp/blog/2012/11/5373/)