開発^3

Web開発、宇宙開発、ゲーム開発の3種類についてつらつらと

Canvas + JavascriptでAmazonの本を立体化の道のり

Amazonの本を立体化する - 開発^3


こんなのは出来たのですが、結局Canvasのセキュリティ対策が引っかかって、Greasemonkey Scriptにはならなかったわけです。
CanvasのgetImageData/toDataURIはセキュリティの都合上、別サイトから画像を読み込んだ場合にはNS_ERROR_DOM_SECURITY_ERRを返すようになっています。

問題発生その1

まずはローカル(d:\program\html\amazon3dbook\test.htm)で書き書きと。
サンプルはAmazonからDLして同じフォルダにおいた画像。

問題:drawImageした場合、getImageDataでNS_ERROR_DOM_SECURITY_ERRが発生する

同じサーバなら問題無いはずだったのだが、file:///でアクセスしている場合、同じサーバとみなしてくれないらしい。

対処:だいぶ嵌った後に、ローカルのWebサーバ上で動作させたらあっさり動いてorz

問題発生その2

画像の生成はできた。Amazonで動くGreasemonkey Scriptにでもしようか。と考えた所。

問題:Amazonの画像ファイル。実は画像だけ別サーバなのですorz

GreasemonkeyScriptはhttp://www.amazon.co.jp/で動く。
画像はhttp://ec2.images-amazon.com/にある。
一見同じ画面にあるから同じサーバに思えるが、drawImage($("prodImage"), ...);とCanvasに描画すると、
描画自体は問題なく行える物の、getImageDataがセキュリティ例外を返すorzorzorz

(結局はダメだったがこの時思いついた)対策:しかし、GreaseMonkeyの場合、どうしても別サーバにアクセスしたい場合はGM_xmlhttpRequestという手がある。(元ページからアクセスされるとまずいので、脆弱性の元にならないように注意が必要だが)

試験用にちょっと確認

http://localhost/test.htmからhttp://localhost/test.jpg
×http://localhost/test.htmからhttp://127.0.0.1/test.jpg

上記のように同じホストでも表現が違うとエラーが起きることを確認できたので、試験にはこれを使用する。


localhostからGM_xmlhttpRequestを用いて127.0.0.1にあるjpegファイルのバイナリデータを取得。
うむ。alertで出しても見事にわけがわからない。

このバイナリデータはjpegファイルの中身そのまま。canvasへ描画する方法が無い。
ということで、DataURIへ変換→<img>タグのsrcにセット→drawImage→getImageDataを試みる。

問題発生その3

base64への変換が必要だがfirefoxにはbtoaという便利な関数があるらしい。


早速btoaで変換してためしに<img>タグに放り込んで表示してみる。
問題:btoaでの変換時にNS_ERROR_DOM_INVALID_CHARACTER_ERR

firefox標準で使えるbtoaはバイナリデータとか、2バイト文字とかには使えないっぽい。
javascript:alert(btoa("あ"));でエラー出たりとか。

対策:バイナリに対応したBase64変換モジュールを使用

JavaScript で Base64 の符号化と復号化
これが対応していたので使わせて頂く。

問題発生その4

無事画像を読み込んでの表示が完成。
さて、drawImage→getImageData・・・

問題:getImageDataでやっぱりエラー

data://スキーマからhttp://スキーマへのコピーだからだろうか。
外部から読み込まれているとみなされて、getImageDataが動作しないorz

対策:・・・無し、かなぁ。
Jpegのバイナリは読めるのでそれを解析してDIBとして放り込み、putImageDataで描画とかすればいけるだろう。

不可能じゃないけど厳しいなぁ・・・と、そいえばゼロが卒業研究で必要に駆られてJpeg読み込んでたな。
JavaScriptJpegライブラリとか作れば何とか・・・かな。

結局

まぁ、冬もあるのでこの辺りでとめておいた。
CanvasのセキュリティやXHR使ったバイナリ読み込み、btoaの問題、外部ライブラリのGreasemonkey組み込みと色々学べておなかいっぱい。

まとめ

○同じサーバの画像を<img>タグで読み込み、drawImage($("img"), ...);とした場合

×file:///D:/test.htm上でfile:///D:/test.jpgをdrawImage(これは何故ダメなんだろう?localhostなら動くのに。)

×同じサーバの画像だがサーバ名指定が違う(http://localhost/test.htmの中でhttp://127.0.0.1/test.jpg)

×DataURIの画像をdrawImage(httpスキーマからdataスキーマの画像を使っている。とみなされるのかな)