前々回でも書いていましたが、
アニメGIFを再現する際の
disposalMethodなる処理がよく分からず、
放っておいたままでした(^_^;)
改めてさらに調査して試してみたら
うまく行った感じになったので、
それについてまとめてみたいと思います。
こちらによると、
Disposal Method は0~3の4種類がある模様です。
Disposal Method
0 - No disposal specified.
The decoder is not required to take any action.
1 - Do not dispose. The graphic is to be left in place.
2 - Restore to background color.
The area used by the graphic must be restored to the background color.
3 - Restore to previous.
The decoder is required to restore the area overwritten
by the graphic with what was there prior to rendering the graphic.
上記のように仕様が書かれているようですが、
実装するにはどうしたらよいのかがよく分からない感じです。
こちらのページでは、
実際に0~3の値を指定して具体的なちがいを説明しています。
ところがGIFをダウンロードしてdisposalMethodの値を確認してみると、
0,1は良いのですが、
「2」となっているのは実際には3で、
「3」となっているのは実際には7が入っていました。
ということで残念ながら間違っている感じです。
ところで、
disposalMethodはアニメGIFを再現するにあたっての、
現フレームから次フレームへ移行する際の
「後始末方法」を指示しているようです。
実装時には先頭以外の各フレームを描画する直前で対処することになりそうです。
値としては0~7を指定できるのですが、4~7は未定義っぽい?
いろいろ調べた結果、
こちらのページを見つけました。
ページの右端に表示されてる5つのフレームを使って、
disposalMethodのちがいによる効果を説明しています。
2つのアニメGIFのdisposalMethodは、
それぞれ順に各フレームで
左:1,2,2,2,2
右:1,3,3,3,3
となっていました。
これによって次のことが分かりました。
Disposal Method:
0) 特に何もしない。
1) 処理時には何もしないが、
”no disposal”という形でフレームの内容を覚えておく。
一番最近に処理したのを1つだけ覚えておけばよい。
2) 全体を背景色でいったん塗りつぶす。
ここで言う「背景色」はGIFヘッダでの設定を指しているのでは無く、
描画側が設定する背景色を指します。
通常は rgba(0,0,0,0) になるので、
描画する範囲の既存の状態が背景色となります。
特に意識しなくても問題ないと思います。
たいていの場合はhtmlのbodyカラーが背景色になるはずです。
3) 2と同様に全体を背景色でいったん塗りつぶし、
覚えておいた”no disposal”なフレームがあればそれを描画する。
思ったより複雑ですね。
仕様書を読んだだけじゃ分からんンわ・・・(^_^;)
ということで、
ライブラリを更新しました。
諸般の都合で一部の仕様が変わります(^_^;)
Gif.createFrameImages()
は以下のように変わります。
第1引数にはCanvasの’2d’なコンテキストを渡します。
第2引数にtrueを渡すと、
あらかじめレンダリングしたイメージを生成します。
通常はこれが使いやすいかと思われます。
falseを渡した場合は、
GIF内のイメージのままで返します。
前フレームからの差分イメージになっていることがあります。
第3引数にfalseを渡すと、
canvasContext.putImageData()
向けのイメージを生成します。
trueを渡すと、
canvasContext.drawImage()
向けのイメージを生成します。
サンプルなソースコードの例は以下のように変わります。
function animateGIF(arrayBuffer,canvas) {
var gif,context,frames,idx,delay,lastTime;
gif = new Gif();
gif.onparse = function() {
start();
};
gif.onerror = function(e) {
alert(e);
};
gif.onprogress = function(e) {
console.log('Parsing...' + ((100 * e.loaded / e.total)|0) + '%');
};
gif.parse(arrayBuffer);
function start() {
canvas.width = gif.header.width;
canvas.height = gif.header.height;
context = canvas.getContext('2d');
frames = gif.createFrameImages(context,true); // pre-rendering
idx = 0;
delay = 0;
draw(); // first draw
if (frames.length > 1) {
setTimeout(function() {
lastTime = performance.now();
tick(lastTime);
},0);
}
}
function draw() {
var frame;
frame = frames[idx];
delay += frame.delay * 10; // 1/100sec to millisec
context.putImageData(frame.image,frame.left,frame.top);
}
function tick(time) {
var delta;
delta = time - lastTime;
lastTime = time;
delay -= delta;
if (delay <= 0) {
idx = (idx+1) % frames.length;
draw();
}
requestAnimationFrame(tick);
}
}
拙作のライブラリを使って、
作ってみたツールはこちらから試せます。
ライブラリのダウンロードも行えます。