BMPファイルフォーマット(Windows)

作成:

前回までにBMP画像について、BMP形式の中でも最も利用されているであろうフォーマットに絞って説明を行った。 対応するフォーマットを限定したことで入出書力処理も非常にシンプルでわかりやすい形にまとまっていたと思う。

今回は、前回まで無視していたBMP形式の他のバリエーションについて説明していこうと思う。 すでに説明した項目について除外するとかえってわかりにくくなると思うので、それらも含めて改めて説明する。

まず前提としてであるが、BMP形式は単純に画像ファイルのフォーマットというよりも、 画像を取り扱うアプリケーション、デバイス間で使用される汎用画像フォーマットとしての側面もあり、 その内の一部形式をデータとして書き出し、ファイルフォーマットとしてそのまま利用しているのがBMP画像ファイルである。 そのため、MSDNなどで様々な定義がなされていても、 それを書き出して画像ファイルフォーマットとして通用するかどうかはまた別の問題である。

このフォーマットはバリエーションが豊富であり、 実際に利用しているアプリケーションごとに対応状況が異なっていたりする。 そのため、他の項目についても同様ではあるが、 ここでの説明は、できる限りMDSNなどの公式のドキュメント等を参照するようにはしているが、 私の予想や思い込みなどが多分に含まれる可能性を予め断っておく。

ファイルヘッダ

全てのBMPフォーマットで共通となるファイルヘッダである。 これは簡易版でも説明したが以下の構成になっている。

typedef struct tagBITMAPFILEHEADER {
  WORD  bfType;
  DWORD bfSize;
  WORD  bfReserved1;
  WORD  bfReserved2;
  DWORD bfOffBits;
} BITMAPFILEHEADER, *PBITMAPFILEHEADER;

MSDNでは BITMAPFILEHEADER structure で説明がある。

bfType

BMP形式であることを認識するために利用される、 いわゆるマジックナンバーやフォーマット識別子、シグネチャなどと呼ばれるものである。

たった2Byteなのでここだけで判断するのは危険ではあるが、 少なくともここの2Byteの値が異なっていればBMP形式ではないと判断できる。

"BM" という文字列が格納される。 バイナリエディタなどでBMPファイルを開いてみるとBMという文字列から始まるデータであることが確認できるだろう。 B=0x42 M=0x4D の順で格納されており、この値をそのまま WORD 型に格納した場合。 リトルエンディアンの環境であれば、値としては 0x4D42 と解釈される。

bfSize
ファイル全体のサイズを表している。 しかし、単体のファイルとなっているのであれば、ファイルの情報からサイズが分かるし、 他の画像自体のサイズ情報などから計算できる値であるため、必ずしも必要な情報というわけではない。 そのため読み込む際は無視しても特に問題はない。 もちろん、ここに正しい情報が入っていることが期待して動作するアプリケーションもあるため、 書き出す際には適切な値を指定する必要がある。
bfReserved1 / bfReserved2
予約領域なので読み込む際はどんな値が入っていようと無視する。 逆に書き出す際は必ず0を書き出すようにする。 現時点ではこの領域を利用するような拡張は行われていないようではあるが、 0 以外の値を出力してしまうと将来的に何らかの意味が与えられた時、おかしな解釈をされてしまう可能性がある。
bfOffBits

ファイルの先頭から画像情報までのオフセット「バイト」数である。 名前は Bits だがバイト数であるところは注意。

この値は読み込む際には重要になる。 ファイルヘッダのあとには情報ヘッダが続き、更にその後ろにはカラーパレットなどのオプション情報が格納されるのであるが、 それらの情報は可変長であるし、それら以外の付加情報が格納されている可能性もある。 ヘッダの拡張情報は基本的には対応していないデコーダは無視しても大きな問題は発生しないように工夫されるため、 この値を参照して適切に読み飛ばすようにす実装するのが良い。

このように、画像ファイルとして必要な情報はないといってよいだろう。 画像ファイルとしてのヘッダ情報というより、画像情報を格納したコンテナのヘッダという位置づけの情報が入っている。

情報ヘッダ(BITMAPINFOHEADER)

ファイルヘッダに引き続き格納されるのは情報ヘッダである。 この情報ヘッダは BMP のバージョンによって様々な種類があるが、 まずは最も多く使われているであろう BITMAPINFOHEADER から説明する。 いわゆる Windows ビットマップで使われるヘッダである。

typedef struct tagBITMAPINFOHEADER {
  DWORD biSize;
  LONG  biWidth;
  LONG  biHeight;
  WORD  biPlanes;
  WORD  biBitCount;
  DWORD biCompression;
  DWORD biSizeImage;
  LONG  biXPelsPerMeter;
  LONG  biYPelsPerMeter;
  DWORD biClrUsed;
  DWORD biClrImportant;
} BITMAPINFOHEADER;

MSDNでは BITMAPINFOHEADER structure に説明がある。

biSize
このヘッダのサイズ、 BITMAPINFOHEADER であれば 40 となる。 情報ヘッダには様々なバリエーションがあるが、このサイズ情報は共通して必ず先頭にあり、 このサイズを見ることでヘッダのバリエーションなどを判別できるようになっている。
biWidth
画像の横方向のピクセル数。 型としては符号付き整数であるが、正の値でなければならない。
biHeight

画像の縦方向のピクセル数。 高さについては負の値も許されている。

負の値の場合は画像情報の縦方向の格納順が逆となり、上から下の方向に格納され、高さはその絶対値となる。 通常の下から上へ格納するボトムアップ形式に対して、この方式をトップダウン形式という。 フォーマットの定義上はどちらの方式もありうるのだが、 互換性に問題があるためトップダウン形式は推奨されていない。 さらに、トップダウン形式でのランレングス圧縮は定義されていない。

読み込む際には両方に対応し、出力する際にはボトムアップ形式とするのが良いだろう。

また、実際に使用することはないだろうが、符号付き整数は負の値の幅のほうが大きい。 具体的に言うと 32bit 符号付き整数では、 -2147483648 ~ 2147483647 の範囲を表現できるが、 絶対値 2147483648 を持つ負の数は表現できるが、正の値を表現することはできないため、この値は不正となる。

biPlanes
画像プレーンの数を表す。この値は 1 でなければならない。
biBitCount

1ピクセルを表現するのに使われるビット数。 有効な値は 0, 1, 4, 8, 16, 24, 32 である。 後述の biCompression が BI_JPEG / BI_PNG の場合のみ 0 が指定される。

具体的な画像情報の格納方法はこの biBitCount と続く biCompression との組み合わせで決まる。 8 以下の数についてはカラーパレット方式となる。 24 の場合は RGB 各色 8bit の BGR の順に格納される。 16 の場合、次の biCompression の値が BI_RGB のとき、 RGB555 つまり各色 5bit ずつ格納されたビットフィールドとなる。 16bit に対して使用するのは 15bit なので 1bit 余り、この1bitは使用されない。 biCompression が BI_BITFIELD の場合、 RGB555 もしくは RGB565 となる。 そのうちどちらなのかは後述するカラーマスクを読み出すことで判断する。 32 の場合、 BI_RGB のとき、 BGR の順に 8bit ずつ使用し、最後に 8bit の Reserve が入る。 つまり色深度としては 24bit と全く同じで、 1 ピクセルあたり 8bit 無駄に使用する。 アルファ値として使用しても良さそうだが、このフォーマットでは使用しない。 biCompression が BI_BITFIELD の場合、カラーマスクを参照することになるがやはりアルファチャンネルは存在しない。

biCompression
Compression という名前から圧縮という意味になりそうだが、 どちらかと言うと各ピクセルの格納方式を表現するパラメータである。 取りうる値は BI_RGB(=0) / BI_RLE8(=1) / BI_RLE4(=2) / BI_BITFIELD(=3) / BI_JPEG(=4) / BI_PNG(=5) がある。 うち、 BI_JPEG / BI_PNG については、 BMP をコンテナとして内部に JPEG や PNG を格納する方式である。 つまり、中身については BMP でもなんでもなく JPEG や PNG となるし、かなり特殊な用途であるので、以降説明は割愛する。 BI_RGB はデフォルトの格納方法である。有効な biBitCount 全てで取りうる値である。 BI_RLE8 / BI_RLE4 はランレングス圧縮という圧縮方法で格納されていることを示す。 それぞれ BI_RLE8 は biBitCout が 8 、 BI_RLE4 は biBitCount が 4 の時のみ取りうる値である。 BI_BITFIELD は biBitCount が 16 及び 32 の時に取りうる値で、 ヘッダの後に RGB 値の格納方法を示したビットフィールド(カラーマスク)があり、その値を使って読み出しを行う。
biSizeImage
ヘッダを除いた画像領域のデータサイズ。読み込むときには必ずしも参照する必要はない。
biXPelsPerMeter
横方向の解像度情報。 画素数という意味の解像度ではなく、画素密度を表す。 この意味では ppi という1インチあたりのピクセル数を表す数字がよく利用されるが、 ここに格納するのは1メートルあたりのピクセル数である。 解像度情報を扱わない場合は0を格納する。
biYPelsPerMeter
前項目と同様に縦方向の解像度情報。
biClrUsed
使用する色数。カラーパレット方式の時に使用される。それ以外の場合は0になる。 また、カラーパレット方式の場合に0が指定されると、 biBitCount で示されたビット数の最大色数として解釈する。 すなわち biBitCount が 8 の時は 256 、 4 の時は 16、 1 の時は 2 である。
biClrImportant
重要な色数。 0 の場合 biClrUsed と同じになる。 biClrUsed と同じ値を出力しても問題ない。 この値が意味を持つ使い方として私が唯一知っているのが Windows98 の起動画面である。 今からすると Windows98 は相当古い OS なので知らない人も多いと思うが、 いわゆる起動アニメーションとして、画像下部のグラデーションで表現されたバーが移動するのだが、 この重要な色数の範囲外の色を順繰りにシフトさせることで実現されていた。 私が利用したことのある最古の Windows が Windows98 であるため、それ以前どうだったかは知らない。

ざっと説明すると上記のようになる。 特に複雑なのが biBitCount と biCompression の組み合わせによって表現される、画像データの格納方法であろう。 この部分については、画像データを説明するときに詳しく説明しようと思う。

カラーパレット(RGBQUAD)

biBitCount が 8 以下の場合、情報ヘッダに続いて、カラーパレットが配置される。 カラーパレットの各色データは RGBQUAD という構造をしている。 MSDNでは RGBQUAD structure に説明がある。

typedef struct tagRGBQUAD {
  BYTE rgbBlue;
  BYTE rgbGreen;
  BYTE rgbRed;
  BYTE rgbReserved;
} RGBQUAD;

一色あたり 4Byte を利用し、 BGR の順に格納、最後の 1Byte は予約領域である。 これが biClrUsed で指定された個数配置される。

カラーマスク

biBitCount が BI_BITFIELD の場合、 各色を読み出すためのカラーマスクがカラーパレットの代わりに情報ヘッダの次に配置されることになる。 カラーマスクは各々 DWORD 値、すなわち 32bit であり、RGBの順で格納される。 合計で12Byteあることになる。 RGB 値の場合は BGR の順であるのに対して、これは RGB の順であるので注意。 カラーマスクの具体的な例としては以下の様な値が入っている。

00 f8 00 00 e0 07 00 00 1f 00 00 00

これを順に読み出し、 32bit ずつリトルエンディアンで解釈すると

0x0000f800
0x000007e0
0x0000001f

となる、いずれも 16bit の範囲で表現できるので、その範囲で更に2進数で書き直すと、

1111 1000 0000 0000
0000 0111 1110 0000
0000 0000 0001 1111

これはいわゆるビットマスクになっている。 上記の例は biBitCount が 16 の場合のカラーマスクの値だ。 RGB565 、つまり、 Red と Blue が 5bit 、 Green が 6bit の色深度を持ったフォーマットである。 画像情報のピクセルデータが以下だった場合、

f0 0f

順に読み出し、 16bit をリトルエンディアンで解釈すると 0x0ff0 となる。 これを前述のカラーマスクを適用すると、

0000 1111 1111 0000 0x0ff0
1111 1000 0000 0000 RMask
0000 1
R = 0x01

0000 1111 1111 0000 0x0ff0
0000 0111 1110 0000 GMask
      111 111
G = 0x3f

0000 1111 1111 0000 0x0ff0
0000 0000 0001 1111 BMask
             1 0000
B = 0x10

と言った形で、 R=0x01、G=0x3f、B=0x10 という値が読み出される。 これら値は 5bit もしくは 6bit 深度の値になるため、 8bit 値に正規化する(0xff/0x1fもしくは0xff/0x3fをかける)と、 R=8、G=255、B=132、つまりこんな色を表してると読める。 biBitCount が 32 の場合も各マスクが 32bit の範囲に拡張されるというだけで読み出し方は同じである。

この処理をプログラム上で実現すると、必要な処理は、 データを読み出し、ビットマスクをかけ、右の0ビットの数だけ右シフト、 ビットマスク内のビットが全て立っていた場合を最大値として 8bit 値に正規化。となる。

biBitCount が 16 もしくは 32 で BI_RGB の場合はカラーマスクのデータは存在しないが、 読み出し方としては同様であるため、デフォルトのカラーマスクを用意し、同じ処理を走らせることになる。

以上が現在 BMP 形式として標準的に利用されている Windows ビットマップのヘッダの内容である。 これだけでもかなり複雑なのだが、情報ヘッダには他のバリエーションもするため、更に複雑な仕様となる。