‘プログラム’ カテゴリーのアーカイブ

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を生成できるようになれば、
パラパラ漫画な冊子を造れるようになるかも
だけど、予定は未定・・(^_^;)

responseType=’document’についての考察

 プログラム
 公開日:2016年12月2日 / 更新日:2017年1月10日

★2017-01-10追記。
Chromeで動作確認できたら基本的にOKにしてしまうのですが、試しにEdgeでやったら動かなくなっているヤツを発見しました(^_^;) responseType=’document’でやるとEdgeでは何故か期待通りにならない・・・なんでやねん! htmlなtextをjQueryに渡すのが素直で無難に動くのだが、cross-domainになるため相対パスな内部のurlが404エラーになることがある(そもそもなぜそのurlを参照しに行くのかが不明なんだが・・・)。結局、htmlなtextをDOMParserでdocumentに変換させるやり方にした。こちらに書いてあるようにセキュリティ的にも良さげな感じだし。これなら404参照が無くなってコンソール出力も無くなるっぽい。やれやれだぜ。

★2017-01-09追記。
古いブラウザを考慮する必要がなければ responseType='document' で、古いブラウザを考慮する必要があるなら DOMParserのpolyfillを導入して、htmlなtextをdocumentに変換するのが良さげなのかも。
なんかそんな気がしてきました・・・(^_^;)。

★2016-12-03追記。
せっかく考察してみたけど、
なんかこんなの見つけてしまった・・・。
こっちの方が良いのかも(^_^;)
ちょっと込み入っている感じだけど。

 どっかのサイトのページに書かれている情報を抽出したいとします。
JavaScriptでやろうとすると、
XHR(XMLHttpRequest)でページを読み込んで、
jQueryを使って解析するという手が思いつきます。
ただCross-Domainな問題があるので、
PHPで作ったproxy経由とかで回避する必要があります。

 jQueryを使ってHTMLなdocumentから情報を抽出するには、
jQuery( selector, context ) とかやります。
通常は、現在のdocumentを対象にすることが多いので、
contextの指定を省略することが多いと思います。
読み込んだページを対象にしたい時はcontextに指定すれば良いわけです。

 contextとして指定できるのは、
documentオブジェクトだけでなくtextも可能です。
textで渡した場合は、jQueryの内部でdocumentオブジェクトに変換されます。
responseTypeに何も指定することなくXHRで読み込むと、
ページの中身がtextで返ってくるので、そのまま渡せばOKです。

 さて、
ここから掲題の件に入ってきます。
とあるアプリを改修すべく、Chromeのデバッガを眺めていたら
なんか404なエラーが起きていることに気が付きました。
放っておいても良さげな感じだったのですが、
ちょっと気になったので調べてみました。

 現象としては次のような感じ。
対象サイトのドメインをA、自サイトのドメインをBとします。
読み込んだページのHTMLをjQueryで解析しようとすると、
その中に相対パスな画像があった場合に、
本来なら「A/パス」と成るところが「B/パス」と展開されて、
そんな画像は無いので404エラーが起こっているようです。
jQueryで解析しようとするだけで、
何故そのパスを参照しに行くのか? 謎である。
しかも画像だけっぽい?

 結局、原因は不明ですが、
textのままだとどのサイトから読み込んだかはわからないわけで、
相対パスな問題が起こってしまうのかもなので、
ドメインな情報も含んでいるはずの
documentとして読めばいいんじゃね?
ということで、
responseType='document'なXHRで読み込んだら
404なエラーは出なくなりました。
めでたしめでたし。
jQueryの内部変換とかも無くなるはずだから、
こっちの方がオーバーヘッドは少ないことになるかもしれませんね。

 これで解決という感じだったのですが、
古いブラウザでうまく動かないことに気が付きました。
IE8とかはもうどうでも良いので切ってしまって構わないのですが、
LTS(長期サポート)なLinuxデストリビューションだと、
prestoなOperaとかが標準ブラウザになってたりすることがあります。
具体的にはLinuxBeanとかがそれに該当します。
自分も使っているので、どうにかしたいなと(^_^;)

prestoなOperaでは、
XHRが responseType='document'に対応してないっぽいようです。
なのでtextとして読めばよいのですが、
そうするとChromeで404エラーが起きることになります。
要するに、
ブラウザが responseType='document'
対応しているか否かをあらかじめ判定できれば、
すっきり解決できそうです。

 そんなわけでWebを漁ってみたのですが、
良さげな解決法が見つかりませんでした。

 ところで、
XHRのレスポンスには responseXMLというのがあります。
XMLでparseできた場合にここに値が入るようです。
responseType='document'でXHRすると、
responseだけでなくresponseXMLにも同じ値が入るようです。
古い版のXHRとの互換性を図る配慮のようです。
XHRではresponseTypeを指定しなくても、
レスポンスのヘッダによって自動で変換される仕組みがあるようです。
XMLはざっくりHTMLの上位互換なようなモノのはずなので(^_^;)
overrideMimeType('text/xml') とかやれば、
responseXMLにdocumentオブジェクトとしてゲットできるかもしれません。
実際には試してませんが、少々強引な感じがするし、
このページにあるようにresponseXMLは扱いが難しそうな感じです。

 さてさて、
responseType='document'なXHRでは、
おそらく内部でHTMLなtextからdocumentへ変換しているはずです。
その際にレスポンスで得たヘッダの情報とかを加味して
変換しているのだと思います。
ということはDOMParserが使えるかどうかで判定できるのではないか・・・。
うまい具合にこのページに判定するコードが載っていたので、
さっそくやってみました。
結論から言うと、うまく行きました。
ただ今回は、
prestoなOperaと現状で最新なChromeについてのみしか
検証してないのであしからず。

 XHRにこだわってやってたりしますが、
jQuery.ajax({dataType:'xml'}) とかでやれば、
実はあっさり行けたりするのかもしれません(^_^;)

 最後に、
対策コードの概要を以下に書いておきます。

Yahoo!の「電力使用状況API」が9月末で終了

 プログラム
 公開日:2016年7月26日

 拙作の「電力使用状況なブログパーツ」では、
Yahoo!の「電力使用状況API」を使っていますが、
こちらで告知されているように、
9月いっぱいで終了となる模様です。

 先の東日本大震災に伴い、
期間限定でサービスされてきたわけですが、
いよいよ終了となるようです。
今夏の節電要請は見送られたし、
一定の役割は果たしたものと判断されたのでしょう。

 ところで、
以前からおかしいなぁと思っていましたが、
東北電力や中部電力の更新停止も告知されてました。
もっと早く告知があっても・・・(^_^;)

 ということで予定通り行けば、
電力使用状況なブログパーツも
2016年9月30日をもってサービス終了となります。

Google Maps API Key

 プログラム
 公開日:2016年7月3日

 Google Maps API を使った拙作のアプリにおいて、
ここ最近なんか警告が出てるっぽい・・・。

 RetiredVersion が出るのは理解できます。
以前のGUIが好みなので、
古いバージョンを使うように意図的に指定しているからです(^_^;)
しかし、NoApiKeys が腑に落ちない。
確かAPIキーは不要だったはず・・・。

 と思ったのですが、調べてみたら
最近になってAPIキーが必須になった模様・・・。

 ということで、
こちらを参考にキーを取得してみました。
HTMLのソースを見ればキーはバレバレなので、
特定のURLにおいてのみ使えるように、
必ずリファラは設定するようにします。
例えば example.com/map.html で公開しているなら、
example.com/* とか設定します。
リファラは複数個を設定できるので、
localhost でテストするようなら、
localhost/*あるいは 127.0.0.1/* とか追加しておくと便利かも?
勝手にキーを借用されてもローカルならたぶん問題ないと思う(^_^;)
気になるようなら一時的に追加して後で削除すれば良いだろうし。

 なお、
設定を更新しても反映されるまで5分くらい掛かるらしいので、
少し待ってから確認の作業をした方が良さそうです。
急いでやると「アレッ?」って感じになりますので(^_^;)