Un nouveau monde parfumé

香り立つ備忘録

0pxの画像ファイル

0px.org というドメインを持っています。このドメインはなるべく短く、かつ意味ありげなドメインが欲しいと思って選んだだけで、特に意味はありません。専らサーバーの alias や短縮 URL に使っています。

さて、0px.org で配布すべきリソースとはなんでしょうか。当然 0px×0px の画像ファイルです。しかし、どんな画像編集ソフトでも大抵キャンパスサイズの最小は 1px×1px です。そもそも 0px の画像を許すフォーマットなどあるのでしょうか。というか画像を2次元的なものとするなら、0px×0px の画像は果たして画像なのでしょうか。

……とりあえず 0px 画像の哲学的な問いかけは一旦忘れることとして、思いついたメジャーな画像フォーマットについて仕様上 0px×0px の画像が許されるのか、調べることにしました。

PNG

www.libpng.org

3章 File Structure には PNG ファイルは PNG file signature(固定8バイト)と chunks からなるとされています。そのうち必須となる chunks4.1 Critical chunks に定められています。この中にある IHDR ヘッダーに画像サイズが入っています。

The IHDR chunk must appear FIRST. It contains:

Width: 4 bytes
Height: 4 bytes
Bit depth: 1 byte
Color type: 1 byte
Compression method: 1 byte
Filter method: 1 byte
Interlace method: 1 byte

Width and height give the image dimensions in pixels. They are 4-byte integers. Zero is an invalid value.

したがって、残念ながら画像サイズは 1px×1px が最小であり、PNG では 0px×0px の画像は表現できないことになります。

JPEG

厳密には、 JPEG (ISO/IEC 10918 | ITU-T T.81) は画像の符号化についてのフォーマットであり、今日 .jpg .jpeg の拡張子でやり取りされるファイルフォーマットについては JFIF (JPEG File Interchange Format) として ISO/IEC 10918 Part.5 および ITU-T T.871 に規定されています。また、これを拡張した Exif も広く普及しています。

今回はこちらの W3C 掲載のJPEG 仕様書 および JFIF 仕様書 を参照します。

www.w3.org

JFIF フォーマットは JPEGSOI (Start of Image) マーカーセグメント、JFIF 情報を格納する APP0 セグメントから始まり、画像データをコードした後、EOI (End of Image) マーカーで終わります。

JPEG の画像データは DCT による量子化情報を記述するテーブル DQT セグメント、 ハフマン符号化による圧縮を行うためのテーブル DHT セグメント、実際のスキャンデータを格納する SOS セグメント、スキャンデータの形式を格納する SOF0 セグメントからなり、これらが合わさり画像データを形成します。(最も基礎的な Baseline DCT の場合)

このうち、画像サイズに直接関わるのはピクセルアスペクト比を決める APP0Xdensity, Ydensity (両方 1 で PAR 1:1 となる)と SOFX および Y(スキャンデータのサンプル数)ですが

B.2.2 Frame header syntax

Y: Number of lines – Specifies the maximum number of lines in the source image. This shall be equal to the number of lines in the component with the maximum number of vertical samples (see A.1.1). Value 0 indicates that the number of lines shall be defined by the DNL marker and parameters at the end of the first scan (see B.2.5).
X: Number of samples per line – Specifies the maximum number of samples per line in the source image. This shall be equal to the number of samples per line in the component with the maximum number of horizontal samples (see A.1.1).

後続の表 B.2 に X および Y の範囲が記されていますが、X = 1~65535 Y = 0~65535 です。一応 DNL セグメントの定義も覗いてみますが

B.2.5 Define number of lines syntax

...The DNL (Define Number of Lines) segment provides a mechanism for defining or redefining the number of lines in the frame (the Y parameter in the frame header) at the end of the first scan. ...
NL: Number of lines – Specifies the number of lines in the frame (see definition of Y in B.2.2).

この NL の値も 1~65535 です。よってJPEG でも 0px×0px の画像は表現できません。

一応 JPEG それ自体の仕様では Annex. B.5 Abbreviated format for table-specification data にフレームデータを持たない JPEG フォーマットというのも規定されており、一方 JFIF には APP0 が必須であるため、これに従えば SOI APP0 EOI からなる JPEG ファイルというのも考えることはできます。が、これは画像を持たないということであり、0px×0px を正しく表しているとは言えないのではないでしょうか。

BMP

BMP(Windows bitmap) のファイルフォーマットの仕様について調べようとしたんですが、正直 Wikipedia が一番詳しかったです。Windows の GDI による実装が事実上の標準として扱われているんだと思いますが、正式な仕様のありかを知っている方は教えてください……

BMP ファイルは BITMAPFILEHEADER ヘッダから始まり、直後に画像情報を格納する DIB ヘッダ、ピクセルデータと続きます。まずはメジャーな DIB ヘッダである BITMAPINFOHEADER を見てみます。

docs.microsoft.com

BMP は他の多くのデジタル画像フォーマットと異なり、左下を原点としてボトムアップピクセルデータを記述するのが特徴的です。

biWidth
The width of the bitmap, in pixels.

biHeight
The height of the bitmap, in pixels. If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. If biHeight is negative, the bitmap is a top-down DIB and its origin is the upper-left corner.

この biWidth および biHeight が画像サイズですが、ここに正の値が入ればボトムアップに、負の値ならトップダウンBMPとしては一般的ではない)になります。ということは0は……?たぶん未定義動作でしょう。

より古い DIB ヘッダーの BITMAPCOREHEADER も見てみます。

docs.microsoft.com

bcWidth
The width of the bitmap, in pixels.
bcHeight
The height of the bitmap, in pixels.

こちらは特に負数についての規定はありません。暗に正の値でボトムアップとなるようです。

とはいえ、値の型としては WORD なので、0を入れられます。よって、0px×0px の画像は

         |-------------BITMAPFILEHEADER-------------|-----
00000000  42 4d 1e 00 00 00 00 00  00 00 1a 00 00 00 0c 00
          BITMAPCOREHEADER--------------|---pixel---|
00000010  00 00 00 00 00 00 01 00  01 00 00 00 00 00
                ^^W^^ ^^H^^

こうなります!*1(強調した部分が bcWidth, bcHeight

(誰か Windows プログラミング環境ある人に GDI で実際何が起きるのか確かめてみてもらいたい)

GIF

https://www.w3.org/Graphics/GIF/spec-gif89a.txtwww.w3.org

GIF ファイルフォーマットで必須とされているのはバージョンを示す Header ブロック(GIF87a or GIF89a 6バイト)と画像情報を格納した Logical Screen Descriptor ブロック、ファイル終端の Trailer (0x3B 固定)のみです。

Logical Screen Descriptor の定義は以下です。

c. Syntax.

      7 6 5 4 3 2 1 0        Field Name                    Type
     +---------------+
  0  |               |       Logical Screen Width          Unsigned
     +-             -+
  1  |               |
     +---------------+
  2  |               |       Logical Screen Height         Unsigned
     +-             -+
  3  |               |
     +---------------+
  4  | |     | |     |       <Packed Fields>
     +---------------+
  5  |               |       Background Color Index        Byte
     +---------------+
  6  |               |       Pixel Aspect Ratio            Byte
     +---------------+

i) Logical Screen Width - Width, in pixels, of the Logical Screen
where the images will be rendered in the displaying device.

ii) Logical Screen Height - Height, in pixels, of the Logical
Screen where the images will be rendered in the displaying device.

この Logical Screen WidthLogical Screen Height が画像サイズを規定します。入れられる値について Unsigned とある以外は特に記述は見当たりませんが、0を入れてもいいはずです!

         |-----Header------|----Logical S.D.-----|tr.
00000000  47 49 46 38 39 61 00 00  00 00 00 00 00 3b
          G  I  F  8  9  a  ^^W^^  ^^H^^ pf bc ar ;

0px 画像に表示すべきデータもなにもないわけですから、Logical Screen Descriptor 以降の画像データは含みません。仕様にも "the entity Data may be repeated any number of times, including 0 times."(Appendix.B) と明記されていますから、これで最低限 GIF の仕様は満たしているはずです。

そういうわけで、 0px.org ではより小さく、より正しそうな上記14バイトの GIF 画像を配布することにしました。適当なパスの GET でも固定で 200 と GIF を返すので ping 代わりにお使いください。

画像フォーマットも改めて厳密に調べてみると色々分かって面白かったです。まあ大抵 0px×0px の画像は画像ではないとして考慮されていない雰囲気でしたが…… 当然大半のデコーダ/ライブラリで上に示した例はエラーになると思います。ちゃんちゃん

おまけ

TIFF

ちゃんと読み込んでいませんが、TIFFImageWidthImageLength はデータ型(SHORT or LONG)の指定があるのみで値域が明示されていないので、0px 画像を表現できる可能性があります。TIFF の中に JPEG を入れることも一般的なため、SOI~EOI だけの JPEG を入れて 0px JPEG をつくれるかも知れません。

SVG

ラスター画像でなければ、SVG は 0px×0px の画像を表現可能です。

<svg version="1.1" width="0" height="0" xmlns="http://www.w3.org/2000/svg"/>

例えばこのように書けると思います。

そのほか参考になったサイト

github.com

様々なファイルフォーマットについて、仕様上許される最小サイズのものを集めたリポジトリです。GIF ファイルはこの記事の解説によるもののようで、1px×1pxを Logical Screen Descriptor に書いているようです*2JPEG は最低限 1px が表示できるような DCT テーブルとフレームのみを含んだ 107 バイトのものなのですが、前述のように JFIF には SOI 直後の APP0 が必須なので JFIF 準拠ではない生 JPEG ストリームです。逆にフレームを含まず JFIF 準拠な最小*3を提案できる気がするんですが…… Issue 立ててみようかな

CGファイル概説 目次

GIFフォーマットの詳細 - とほほのWWW入門

JPEG フォーマット、GIF フォーマットに関する日本語文献です。特に JPEG は重厚でちゃんと読めてる自信がないので助かりました。

*1: 表示はしてほしくないが1px分のデータが入っており(001A~001E 1ピクセルあたりのバイト数は4の倍数にアライメントされる)そのオフセットを BITMAPFILEHEADER の bfOffBits に記している

*2:Image Data を含まないためデコーダによってはエラーになる / 特に IE でエラーになることが報告されている

*3:たぶん合計22バイト