今回はJavascriptを使って画像フォーマットの1つであるPNGファイルの情報を一部読み取ってみたいと思います。作っているウェブサービスに必要なものだったので。
PNGのファイルフォーマットは以下のサイトが参考になります。
http://www.setsuki.com/hsp/ext/chunk/IHDR.htm
画像の横幅を取得したい場合は0x0008 (4)ですので0x0008のオフセットに対してUnsigned Int型で取得することで実現できそうです。
実際にバイナリツールの「Bz」を使ってPNGファイルを覗いてみると該当箇所に画像サイズ情報が書かれています。
わかりやすくオレンジの枠で囲っています。「00 00 01 72」= 370px、 「00 00 00 D2」 = 210pxということで、 横370px 高さ210pxの画像であることがわかりますね。
というわけでJavascriptで取得できるかを試そうとしたのですが・・・。
readAsBinaryStringがもう古いらしい
昔バイナリ周りを触っていた時はreadAsBinaryStringを使ってバイナリ操作していた記憶があるのですが、これ素手ラ非推奨になっておりIEではこのメソッド自体が既に消えている模様です。(IEでの動作確認で気づく。)。W3Cからも消されているで消滅するのは時間の問題でしょう。というわけで代わりに使えるreadAsArrayBufferを使いましょう。
参考 :いつの間にかFileAPIのreadAsBinaryStringがオワコンになっていた。今後はreadAsArrayBufferで。
PNGフォーマットの判定を書いてみる
1 2 3 4 5 6 7 8 |
var isPngFormat = function(bin) { var sig = String.fromCharCode(0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a); var head = String.fromCharCode.apply(null, new Uint8Array(bin)).substr(0, 8); if (sig == head) { return true; } return false; }; |
こんな感じでどうでしょう。bin引数はPNGのバイナリデータが入ったArrayBufferです。
横幅を取得してみる
1 2 3 4 5 6 7 8 |
var getPngWidth = function(bin) { if (!isPngFormat(bin)) return null; // これはダメ var fail = new Uint32Array(bin, 0x10, 1); // こっちの方法で取得しないとぽい var data = new DataView(bin); return data.getUint32(0x10, false); }; |
横幅を取得する関数です。Uint32Arrayを使っていたのですが、どうも正しい値を取得してくれない・・・これはリトルエンディアンでバイナリを読み取るための模様。DataViewを使うことで、リトルエンディアン・ビックエンディアンと読み取りを切り替えできるのでこちらを使うことで正しい値を返すことが出来ました。(ただしDataViewは低速らしいですが。)
最後に高さの取得
1 2 3 4 5 |
var getPngHeight = function(bin) { if (!isPngFormat(bin)) return null; var data = new DataView(bin); return data.getUint32(0x14, false); }; |
追記(2015/11/24)
- Int8Array
- Uint8Array
- Int16Array
- Uint16Array
- Int32Array
- Uint32Array
- Float32Array
- Float64Array
これらはバイトオーダがCPUネイティブになる(私はIntel CPUを使っていたのでリトルエンディアンとなっていた)らしい? 取得方式がリトルエンディアンかビックエンディアンかを判定する方法がないと取得用途では使えないような・・。
参考:HTML5のJavaScriptでバイナリファイルを扱う(その2)