15分で読めた気になるPNG
こんにちは
画像、色がすき。
どうしてPNGを読みたい?
この世には様々な画像の形式があり、Gyazoにアップロードできる形式の中でも
JPEG,
PNG,
GIFなどがある。
それらのアップロードされてくる画像には様々なメタデータが含まれていたりするので、それをパースして利用したりするため。
例えば、PNGの場合キャプチャされた画面の
解像度を取得して、画面に出す時によしなにするなどに利用されている。
貴重なメタデータだ

おかしなファイル をアップロードされて500エラーが出たりした時に、どうしてそうなったかをデバッグしたりするため。
時々壊れたファイルがアップロードされてAlertが流れる。
普段扱っているファイル形式については、詳細な
バイナリの上での配置とかまではともかく、おおまかな構造ぐらいはわかっていると良い気がする。
いい話part 2だw


読める、読めるぞ
楽しさが伝わってきてすごくいい...

どうやってPNGを読むか?
バイナリを読んでいくためにはファイルの構造を知る必要がある。
テキストとは違い、見ただけではわからない。
そのためには仕様/specを読むのが良い。
こんなスライドを見てる場合じゃなく、元のspecを読むのが大切!!
これだけ覚えて帰ってください。
発表はこれにて終了。
wwwwwwwwww


とは言え、全体の見通しをなんとなく持っておくのは意味があるので続きをやっていく。
たしかに…w

構造概略
実際のファイルではこんな感じ。
od -c out.png | head
先頭のほう。
最後まで聞けば、これが雰囲気ぐらいは目で読めるといいな。
odコマンド必携ですね

!!P N G!!

オシャレ!

わかりやすい

詳細
ヘッダ(PNG file signature)
固定の8byte 89 50 4E 47 0D 0A 1A 0A
処理系は普通、これを見ることでそのファイルがPNGであることを認識する。
メールでの転送時に最上位bitが落ちることがあったり、転送時に改行コードの変更が変換が行なわれていたりしていることを検知できるように考えられている。
shift_jisで
0x89 0x50
は
臼になる。
チャンクの概形
これは箱の形なのでさらっと流すけれど、 Chunk Type
というものがチャンクの構造としては大切であるということだけを覚えておいてもらえると良い。
Chunk Type
/[a-zA-Z]{4}/
のアルファベット4文字
この4文字でチャンクの種別を判別する。
4文字で名前をつけるとともに、それぞれの文字のcaseに以下の情報が含まれる。
1文字目: 大文字であると、「
必須チャンク」と言い、全ての処理系はそのチャンクを正しく解釈できる必要がある。
逆に、小文字であるチャンク(「
補助チャンク」)のうち、意味を知らないチャンクは無視することが許されている。
これにより、互換性を破壊せずに拡張したりすることが可能となっている。
なるほど


2文字目: spec等で定義されたチャンクは大文字。小文字にしておけば自由なチャンクを定義して使うことができる。
任意のdataを持つことができるので、例えばゲームのセーブデータを保持するようなチャンクを定義して、pngの画像に埋め込むなどの例などが思いつく。
(pngの画像にセーブデータを保存する某ゲームでこういう構造になっているのかと覗いてみたら、 IEND
の後ろに IDAT
として構造を持つようになっていた……。確かに IEND
で普通の処理系は処理を止めるから動くだろうが……という感じがする)
pngにセーブデータ黒魔術感しかしない


3文字目: 将来の拡張に予約されてて常に大文字。
4文字目: safe-to-copyの説明をするには余白が足りない。
w

余白を作りました!(違う)

定数か変数かみたいな考え方とも違うルールで面白い

これには感動した

最小限の領域に如何に情報を詰め込むかを突き詰めている

* 基本的なチャンク
IHDR
: 画像のサイズやビット深度などが入ってる。
IEND
: このチャンクが来るとこの画像はここまでということを表す。
IDAT
: 画像データの「本丸」。ここにピクセルの情報が圧縮されて入る。
フィルタ
これらを解くとpixelの列が得られる。
sub行の中央は0なのかな

おおおおなるほど

実際うまくフィルタを当てるの難しそう

チャンクの列 という抽象化
これがよくできているので、
後方互換性を保ったまま機能を追加できるので良い。
>Future extensions to this specification will not add new fields to existing chunks; instead, new chunk types will be added to carry new information.
もし未来にspecが更新されて、拡張が入ったとしても、既存のチャンクに新たなフィールドが入ったりするようなことはない。そうせずに、新たなチャンクを定義することによって値を保持する。
>Note that there is no version number in the signature, nor indeed anywhere in the file. This is intentional: the chunk mechanism provides a better, more flexible way to handle format extensions
PNGのバージョンナンバーのようなものをどこかのフィールドに入れたりする(それによって構造を定義する)のではなく、チャンクを使うのがフレキシブル。
GIFとかだと、 file signature
相当の位置に、 GIF87a
, GIF89a
などのバージョン番号が入っている。
これは、 GIF87a
しか知らない処理系は GIF89a
であるような画像を受けとった時に諦めるほかないが、後述するようにPNGだとそのようなことにはならない。
2087年に新しいGIFの仕様つくれないのか〜

PNGのサムネイル偽装とかもこういう仕様だからか

読める!!読めるぞ!!
APNGのファイル構造/APNGを理解する処理系から見たフォーマット
APNGを解さない処理系から見た同じファイル
知らない値は無視する方式

チャンクというバージョンに対して不変な構造を用いている
読めない部分は無視できて便利な反面、ウイルスとかも仕込みやすそう…

特定のデコーダーの脆弱性をつくデータとか
それはそれで面白いか
APNG対応の処理系ならアニメーション表示できる、APNG非対応の場合でも静止画を表示できる!

凄いよくできた仕様だ


こんな感じのスケールする仕様すき

もし人生のうちにファイルフォーマットを考えるようなことがあれば、こういった拡張性を持たせられるような形式にしたいと願う。
まとめ
よく使われているフォーマットのバイナリを読むと楽しい。
ファイルの構造を読むにはそのspecにあたるのが大切。
PNGのファイルはヘッダの後にチャンクが並ぶような構造をしている。
この構造は後方互換性を持ったままの拡張に強い。
ステキだ

いろんなバイナリを読んでいこう。
Nota/Gyazoでは画像について詳しいエンジニアを募集しています。
途中で挫折したOTL

挫折の理由の大半が「知らない」
IEND