‘gif.js’ タグのついている投稿

アニメGIF⇒パラパラ漫画(JS版)

 プログラム
 公開日:2017年3月13日

アニメGIFからパラパラ漫画の冊子を作る
という記事に触発されて、
JavaScript版を作ったらどうだろうかと思い立ち、
まずはGIFをparseするライブラリを探してみるも、
個人的にしっくり来るのが見つからなかったので
改造して新たにライブラリを作ったりしました。

 次に必要なのはPDFを生成するライブラリ。
いくつかあるようなのですが、
クライアント側だけでうまくやれそうなのは
jsPDF一択という感じ。

 ただ、
こちらのドキュメントを見ると、
画像を扱うAPIが見当たらない・・・うむむ。
一方、こちらのLiveDemoでは画像を扱ってる・・・???。

 調べてみた所、
ドキュメントにはjsPDFのコアな部分についてのみ書いてあるようです。
画像を扱うための addImage なるAPIは
プラグインという形で提供されているようです。
こちらのGitHubにある、
"dist/jspdf.debug.js" および "dist/jspdf.min.js"
がプラグインを同梱しています。
前者は debug とかなってますが、minimizeされてないだけです。
debug時はこちらを使ってね、くらいな意味合いだと思います。

 さて、
jsPDF.addIamge()には dataURL な画像イメージを渡します。
読み込んだGIFファイルはcanvasに展開することになるので、
canvas.toDataURL()すればbase64なPNGを簡単に得ることが出来ます。
これでどうにかなりそう。
とか思ったんですが・・・実は苦労しました(^_^;)
コマ数が数十枚程度のアニメGIFを処理したら
ブラウザがメモリ不足エラーを起こしました・・・。
PNGをデコードしてPDFで扱えるようにしてるようなのですが、
イメージのサイズの総量が大きすぎるようです・・・。
addImageには圧縮のオプションがあるので、
指定してやってみたらエラーは起きなくなりましたが、
今度は時間が掛かることになってしまいました。

 jsPDFでPNGを扱うのはメモリ的&速度的に
ちょっと厳しいのかもしれません。
さてどうしたものか、と思いながら
addImageのソースを眺めていたら、
分かったことが2つありました。
ひとつはPNGだけでなくJPGも扱えること。
もうひとつは、
PNGはデコードした上で処理する必要があるが、
(圧縮指定があればさらにエンコードが必要)
JPGはほぼそのままで扱えるようになっていたこと。
PDFはJPGのイメージをそのまま扱えるっぽいようです。

 そんなわけで、
canvas.toDataURL('image/jpeg', 0.8)とかやって
addImageに渡すようにしました。
速度的にだいぶ改善された感じがします。
PNGのlosslessに対してJPGはlossyになってしまうので、
画質を設定できるようにしました。
またメモリ不足エラーが起きてしまった場合に備えて、
PDFを複数ページに分割して処理できるような工夫もしてみました。

 ちなみに、
jsなフレームワークの一つである Mithril.js
いつのまにかバージョン v1.0.xとかになってて、
APIとか一新されてました。
よい機会なので今回使ってみた次第です。
v0.2.xは使ったことあるのですが、
v1.0.xはいろいろ変わってたりしてました。
どう変わったかについては別途書いてみるつもりです。

 ということで、
アニメGIF⇒パラパラ漫画(JavaScript版)は
こちらから試せます。

ところで、
自分は印刷して切り出して冊子を作ったりしてなかったりします(^_^;)
あしからず。
実際にやってみた方の感想とかいただけるとうれしいかも。

GIFをparseするjsなライブラリ(3)

 プログラム
 公開日:2017年3月6日

 前々回でも書いていましたが、
アニメGIFを再現する際の
disposalMethodなる処理がよく分からず、
放っておいたままでした(^_^;)

 改めてさらに調査して試してみたら
うまく行った感じになったので、
それについてまとめてみたいと思います。

 こちらによると、
Disposal Method は0~3の4種類がある模様です。

 上記のように仕様が書かれているようですが、
実装するにはどうしたらよいのかがよく分からない感じです。

 こちらのページでは、
実際に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()向けのイメージを生成します。

 サンプルなソースコードの例は以下のように変わります。

 拙作のライブラリを使って、
作ってみたツールはこちらから試せます。
ライブラリのダウンロードも行えます。

GIFをparseするjsなライブラリ(2)

 プログラム
 公開日:2017年2月28日

 先日、
GIFをparseするjsなライブラリを作ったりしましたが、
parseするのにそれなりな時間が掛かったりするので、
progress表示とかがうまくやれない感じ。
そんなわけで、
web worker を使って別スレッドで動かすことで、
非同期でやれるように作り直してみることしました。

 ところで、
web worker を使うには、

とかやって、
別なソースファイルからworkerを作るのが普通だと思います。

 workerを使うのは久しぶりだったので、
あらためて使い方なぞをネットで調べていたら、
ここここに書いてあるような
inline worker という手法があることを知りました。
実質的に以下のようなことが出来てしまいます。

個人的にこんな発想はありませんでした(^_^;)
なるほど分かりやすい感じになりますね。
ソースファイルを分けなくても行けるのが便利です!

 どうやって実現しているのかと思ったら、
関数のコード ⇒ 文字列化 ⇒ blob ⇒ ObjectURL
とかやってました。

なるほど納得!
うまいこと考えましたね。
ソースコードのイメージを作って、
ブラウザ内のメモリに置いたのをURLで参照という感じかな。

 なお、
上記のページでは、
“text/javascript”なMIMEでblobを作っていますが、
“application/javascript”の方が新しい指定なようです。
どっちでも動くと思いますが。

 同じソースファイルに書けるとは言っても、
workerとして渡す関数は別スレッドで動くので、
必要なコードが完結するようにしないといけません。
主スレッド側に書いた関数とか呼べないので要注意です。
1つのソースファイルに書けてしまうので、
いかにも呼べそうに見えるのが罠です(^_^;)
また、こちらによると、
inline-worker内でimportScripts()する場合は、
絶対URLで指定する必要があります。
相対パスだと例外が起きます。
inline-worker自体はblobスキームで生成されるので、
cross-domainな制約を受けるためだそうです。
相対パスな指定だと blob:扱いになってしまうっぽい。

 ちなみに、
デバッグしていて気付いたのですが、
worker側からErrorオブジェクを送信したら、
Uncaught DOMException: Failed to execute ‘postMessage’ on ‘DedicatedWorkerGlobalScope’: An object could not be cloned.
こんな感じのエラーが起きました。
調べてみても理由がよく分かりませんでしたが、
Errorオブジェクトにはstackトレースが含まれてるためだと思います。
スレッド固有な情報を別なスレッドで処理したらマズイことになりそうだし。

とか思ったんですが、
{message: error.message, stack:error.stack}
こんな感じのオブジェクトを作って返すと例外は起きませんでした。
ということで例外起きる理由はやっぱよく分からない・・・。
しょうがないので文字列化して送信しました(^_^;)

 ということで、
inline worker な手法を取り入れて、
ライブラリを作り直しました。
ここで書いた具体例のコードも以下のように変わります。

 拙作のライブラリを使って、
作ってみたツールはこちらから試せます。
ライブラリのダウンロードも行えます。

GIFをparseするjsなライブラリ

 プログラム
 公開日:2017年2月19日

 先日、
アニメGIFからパラパラ漫画の冊子を作る
という記事を取り上げましたが、
これのJavaScript版を作ったら面白いかも、
とか思い立ちました(^_^;)

 まずは、
GIFを扱うライブラリを探してみたのですが、
個人的に良さげなのが見つかりませんでした。
というかうまく見つけられなかったのかも(^_^;)
そんなわけでこちらのjsgifを元に作ってみることにしました。
GIFフォーマットの詳細はこちらを参照しました。

 元になったコードでは、
バイナリ文字列という手法でGIFイメージを扱っていますが、
これだと読み込みがXHR限定になってしまうし、
少々トリッキーで余計な処理も必要となる感じ。

 そこで、
arrayBufferでイメージを扱うように改造しました。
responseType='arraybuffer'なXHRや、
Drag&Drop経由の FileReader.readAsArrayBuffer()
読み込めるようになるので扱い易くなるのではないかと。

 また、
オリジナルではGIFイメージのchunk(データの塊)を走査する毎に、
指定があればコールバックを呼び出す作りになっていましたが、
直感的に扱いにくい感じだったので、
単純にparseした結果を返すように変えました。

 さらに、
canvasなコンテキストで扱うのに都合が良い
画像イメージを作成する機能を加えてみました。
ただ、透明ピクセル時に必要となるっぽい
disposalMethodなる処理が良く分かってないです(^_^;)

 なお、
GIFファイルのサイズが大きいとか、
コマ数が多かったりすると、
LZWなデコードを伴うせいか、
parseするのにそれなりな時間が掛かるようです。
WebWorkerとか使って別スレッドでやったほうが良いのかも?(^_^;)

 さて、
拙作のライブラリを使うには、
例えば以下のようにします。
GIFのイメージを読み込んだarrayBufferと
イメージを描画するためのcanvasを渡します。
アニメーションさせるには setTimeout()を使うのが簡便そうですが、
requestAnimationFrame()を使ってVSYNCな感じになるようにしてみました。

Gif.createFrameImages()について説明します。
第1引数にはCanvasの’2d’なコンテキストを渡します。
第2引数は値によって生成されるイメージが次のようになります。
・0の場合
  canvasContext.putImageData()向けのイメージを生成します。
・1の場合
  canvasContext.drawImage()向けのイメージを生成します。
 通常はこれが使いやすいと思います。
・2の場合
 1の場合と同様ですが、あらかじめレンダリングしたイメージを生成します。
 0と1の場合は、前フレームからの差分イメージになっています。

 ということで、
ちょっとしたツールを作ってみました。
アニメGIFをドロップすると各コマに分割してダウンロードできます。
試すにはこちらからどうぞ。
ライブラリのダウンロードも行えます。

 あとはPDFを生成できるようになれば、
パラパラ漫画な冊子を造れるようになるかも
だけど、予定は未定・・(^_^;)