generated at
15分で読めた気になるPNG
こんにちは
Gyazoのサーバーサイドをやってる。
画像、色がすき。
いい話だakixdaiiz

どうしてPNGを読みたい?
この世には様々な画像の形式があり、Gyazoにアップロードできる形式の中でもJPEG, PNG, GIFなどがある。
それらのアップロードされてくる画像には様々なメタデータが含まれていたりするので、それをパースして利用したりするため。
例えば、PNGの場合キャプチャされた画面の解像度を取得して、画面に出す時によしなにするなどに利用されている。
貴重なメタデータだakix
おかしなファイル をアップロードされて500エラーが出たりした時に、どうしてそうなったかをデバッグしたりするため。
時々壊れたファイルがアップロードされてAlertが流れる。
あるあるakixyuisekiyuiseki
普段扱っているファイル形式については、詳細なバイナリの上での配置とかまではともかく、おおまかな構造ぐらいはわかっていると良い気がする。
バイナリを読むと楽しいから。
いい話part 2だwakix
hiroshi 読める、読めるぞ
楽しさが伝わってきてすごくいい... daiiz
Kaitai Structはいいぞ

どうやってPNGを読むか?
バイナリを読んでいくためにはファイルの構造を知る必要がある。
テキストとは違い、見ただけではわからない。
そのためには仕様/specを読むのが良い。
こんなスライドを見てる場合じゃなく、元のspecを読むのが大切!!
これだけ覚えて帰ってください。
発表はこれにて終了。
wwwwwwwwwwakixutgwkk
とは言え、全体の見通しをなんとなく持っておくのは意味があるので続きをやっていく。
https://ja.wikipedia.org/wiki/Portable_Network_Graphics の記事は、ざっと見るにはよくまとまっているが、これを読んですぐにわかるならそれはもうわかっている人だと思う。
たしかに…wakix
これをアクティブ読書しようtakker

構造概略
ヘッダがあった後は、chunkの列。
実際のファイルではこんな感じ。
od -c out.png | head 先頭のほう。
最後まで聞けば、これが雰囲気ぐらいは目で読めるといいな。
odコマンド必携ですねyuiseki
!!P N G!!akix
オシャレ!daiiz
わかりやすいutgwkk

詳細

ヘッダ(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文字目: 大文字であると、「必須チャンク」と言い、全ての処理系はそのチャンクを正しく解釈できる必要がある。
逆に、小文字であるチャンク(「補助チャンク」)のうち、意味を知らないチャンクは無視することが許されている。
これにより、互換性を破壊せずに拡張したりすることが可能となっている。
なるほどakixyosider
2文字目: spec等で定義されたチャンクは大文字。小文字にしておけば自由なチャンクを定義して使うことができる。
任意のdataを持つことができるので、例えばゲームのセーブデータを保持するようなチャンクを定義して、pngの画像に埋め込むなどの例などが思いつく。
(pngの画像にセーブデータを保存する某ゲームでこういう構造になっているのかと覗いてみたら、 IEND の後ろに IDAT として構造を持つようになっていた……。確かに IEND で普通の処理系は処理を止めるから動くだろうが……という感じがする)
pngにセーブデータ黒魔術感しかしないtakkerutgwkk
3文字目: 将来の拡張に予約されてて常に大文字。
4文字目: safe-to-copyの説明をするには余白が足りない。
wakix
余白を作りました!(違う)takker




case自体に意味が設定されているのすごいなakixMijinko_SDtakker
定数か変数かみたいな考え方とも違うルールで面白いakix
これには感動した daiiz
最小限の領域に如何に情報を詰め込むかを突き詰めているtakker



* 基本的なチャンク
IHDR : 画像のサイズやビット深度などが入ってる。
IEND : このチャンクが来るとこの画像はここまでということを表す。
IDAT : 画像データの「本丸」。ここにピクセルの情報が圧縮されて入る。
フィルタ
これらを解くとpixelの列が得られる。
おお〜なるほどakixdaiizMijinko_SD
sub行の中央は0なのかなakix
> 元々の範囲から外れている値は0扱い
おおおおなるほどakix
実際うまくフィルタを当てるの難しそうMijinko_SD

チャンクの列 という抽象化
これがよくできているので、後方互換性を保ったまま機能を追加できるので良い。
>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の仕様つくれないのか〜akix
PNGの仕様こんなに天才的だったの知らなかった・・・akixtakkernishiotetsuya-k
PNGのサムネイル偽装とかもこういう仕様だからかmtane0412
>“gAMA”チャンク
読める!!読めるぞ!!

APNGのファイル構造/APNGを理解する処理系から見たフォーマット
APNGを解さない処理系から見た同じファイル
知らない値は無視する方式takker
チャンクというバージョンに対して不変な構造を用いている
読めない部分は無視できて便利な反面、ウイルスとかも仕込みやすそう…Mijinko_SD
特定のデコーダーの脆弱性をつくデータとか
それはそれで面白いかMijinko_SD
APNG対応の処理系ならアニメーション表示できる、APNG非対応の場合でも静止画を表示できる!hata6502
凄いよくできた仕様だ daiiztetsuya-k
こんな感じのスケールする仕様すきMijinko_SD
もし人生のうちにファイルフォーマットを考えるようなことがあれば、こういった拡張性を持たせられるような形式にしたいと願う。
エモいakixdaiizutgwkk

まとめ
よく使われているフォーマットのバイナリを読むと楽しい。
ファイルの構造を読むにはそのspecにあたるのが大切。
PNGのファイルはヘッダの後にチャンクが並ぶような構造をしている。
この構造は後方互換性を持ったままの拡張に強い。
ステキだakix
いろんなバイナリを読んでいこう。
Nota/Gyazoでは画像について詳しいエンジニアを募集しています。

途中で挫折したOTLtakker
挫折の理由の大半が「知らない」
つまりPNGの仕様書をアクティブ読書すればいい!

IEND