仕事で GIF のデータチャンクなんて覗いている。
WEB の画像の多くが GIF だった時代なんかに、GIF をいじるプログラムは何度か作ったことがある。
画像データを直接いじったりするのはご免蒙りたいが、無駄なデータを削除したり、著作権表記のコメントを入れたりね。
今回はそれとも違う理由でデータを覗くプログラムを作っているのだけど、上手く動かないから直接バイナリエディタでファイルを開いたりしながらデバッグしていた。
動かなかったのは自分の勘違いだった。その話はいいんだ。
今日書きたいのは、Photoshop なんかで作った GIF ファイルには XMP というデータが含まれていて、このデータ構造が面白かった、ということ。
XMP は、画像に関する様々なデータを、人間に可読、かつコンピューター処理もしやすい形で、様々な画像フォーマット内に埋め込むための統一された方法。
Adobe が提唱していて、誰でも使ってよいらしいのだけど、Adobe 以外に使っている人がどれほどいるのかは不明。
多くの人にとっては画像に関するデータは EXIF があればいいよ…となりそうだけど、EXIF は JPEG と TIFF にしか埋め込めない。
XMP は、もっと多くの画像ファイルに埋め込めるようにしてある、という点で、一定の利点はある。
で、GIF にも埋め込めるわけだけど、その前に XMP データについてざっと解説しておこう。
まず、XMP は先に書いたように「人間に可読」だ。
テキストファイルになっている。
コンピューターにも処理しやすいように、XML と呼ばれる形式で書かれる。
もっと言えば、XML によって記述される、RDF と呼ばれる形式に従っているのだけど、そういう細かなことはこの際どうでもいい。
XML は < > の記号をやたらと使って書く。HTML の中身を見たことがある人ならわかると思うけど、親戚関係だ。
で、XMP は必ず「<?xpacket ~ ?>」という文字列で囲まれている。
画像ファイルをテキストとみなして検索し、この部分を取り出せばよい。画像ファイルの構造なんて知らなくても扱えるので、お手軽だ。
ところで、GIF ファイルには、コメントとして文字列を埋め込む機能がある。
なーんだ、簡単。じゃぁ、XMP もコメントとして書けばいいわけだ。
…ところが、そうはいかない。
GIF は内部でチャンク(データのまとまり)構造を取っているのだけど、基本的に次のような構造なのだ。
・何を示すチャンクであるかの ID 1バイト
・そのチャンクに必要なデータ 数バイト(ID ごとに構造が決まっている)
さらに、必要に応じてチャンク内のブロックが作られる。
ブロックの数は任意だ。なくてもいいし、何個続いてもかまわない。
・データ長 1バイト
・データ長の長さのデータ
データ長が 0 の時、ブロックはそれで終わりだということを意味する。
逆にいえば、0 にならなければ、いくらでもブロックを続けてよい。
ここで重要なのは、データ長が1バイトだということで、データの長さは最大 255バイトとなってしまう。
先に GIF ファイルにはコメントを埋め込める、と書いたのだけど、255 文字で一区切りだ。
それ以上のコメントが入れられないわけではなくて、内部で複数のブロックに区切って格納される。
でも、先に書いたように、XMP はファイル構造を知らないでも取り出せるテキストとして埋め込まれる。
それでは GIF ファイルのコメントとして適した形にならない。
ここでちょっと工夫が必要になるわけだ。
さて、GIF における XMP の構造。ちょっとした Hack だ。
まず、「データ長1バイト」なんて気にせず、データ長の部分からいきなりテキストファイルを書き始めてしまう。
XMP は結構長くて、普通は 255 文字なんかに収まらないけど、最後まで一気に書ききる。
XMP の先頭は < なので、ASCII コードに従ってデータの長さは 60 ということになる。
GIF フォーマットを読むつもりで見ると、そこまでテキストを読み飛ばして…次の「文字」の文字コードが、次のブロックの長さ、ということになる。
一応、可読テキストなので ASCII コードの「 0 」、いわゆる null 文字は入らない。
だから、チャンクが終了してしまうことはなく、テキストはちゃんとチャンク内に格納されていることになる。
でも、こうやって読み飛ばしていくだけでは、いつかテキストの終わりを飛び越えてしまうだろう。
もちろん、その通り。
XMP テキストは、<?xpacket ~ ?> で囲まれている、と書いたのを思い出してほしい。
最初と、最後にこの文字列が付いている。
逆にいえば、XMP テキストを取り出したい人にとって、その外側にあるデータは関係がない。
そこで、GIF ファイルの XMP データは、テキストの直後に 255 から 0 に至る、1つづつ数の減る 256 byte のデータが埋め込まれている。
GIF ファイルのつもりで読んでいると、いつか XMP テキストの終わり飛び越し、このデータの中に着地する。
データは、1バイト進むたびに、1数値が減っている。
だから、その数値の分だけ先に進むと、どこから始めたとしても、必ず同じ場所にたどり着くことになる。
そして、その数値は「0」だ。
先に書いた通り、データ長が 0 の時は、チャンクの終了を意味している。
そのため、XMP を格納したチャンクは、ここで終わる。
先に書いたように、僕のプログラムミスで GIF ファイルを覗くプログラムがうまく動かなかった。
デバッグのためにバイナリエディタで直接観察したら、こんな構造のデータがあった。
255byte でブロックを作る、という規則を無視して入れられた、とても長いテキストデータ。
最初は、こいつが悪さをしていて自分のプログラムが動かないのではないかと疑った。
でも、他のプログラムは正しく画像を出しているのだから、問題はないはず。
次に考えたのは、XMP 用に特別な構造の追加仕様があって、僕のプログラムがそれに対応できていない、ということ。
でも、これも違う。後から追加するデータ仕様が、それ以前のプログラムの動作と非互換なわけがない。
そう思って冷静に動作を考えたら、上に書いたような巧妙な構造が見えてきた。
この構造の欠点は、256byte も無駄なデータを詰め込んでいることだ。
でも、そもそも XMP 自体が、画像だけを見たい人にとっては「無駄なデータ」だ。
データを入れたい人にとっては必要があるから入れるのであって、その場合は 256byte くらいは誤差範囲だろう。
同じテーマの日記(最近の一覧)
別年同日の日記
申し訳ありませんが、現在意見投稿をできない状態にしています。 |