BMPファイル形式
Windowsで広く使用されているビットマップファイルの、ファイルフォーマットについての解説です。
Windows上でBMPファイルを使用する限りであれば、特に自力で解析する必要はないし、むしろそうすべきではありません。しかし、Windows以外の環境、例えばUNIX系OSでBMPファイルを取り扱う必要がある場合には、全てを独力で処理しなければなりません。
ファイル形式の概略
ファイルは主に以下のような構造をしています。
| BITMAPFILEHEADER構造体 |
| BITMAPINFOHEADER構造体 |
| RGBQUAD構造体の配列 |
| ピクセルのデータ |
まず、BITMAPFILEHEADERとBITMAPINFOHEADERを読み込み、ファイルの種類を判定し、必要ならカラーテーブルを読み込み、ピクセルデータを読み込むという手順になります。
また、下記で使用する構造体や定数はwindows.hをインクルードすると、その中(でインクルードされている「Wingdi.h」というヘッダファイル内で)定義されています。
BITMAPFILEHEADER構造体
BITMAPFILEHEADER構造体は以下のような形式をしています。
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
- bfTypeにはBMという値が格納されています。PC上のリトルエンディアンな環境では0x4D42という値と比較して、開いたファイルがビットマップ形式かどうかを判断することになります。
- bfSizeにはビットマップファイルのサイズが記録されています。しかし、Windows付属のペイントはこの値を参照していません(私の経験上)。信用しない方がいいと思います。
- bfReserved1とbfReserved2は0が記録されています。
- bfOffBitsにはBITMAPFILEHEADER構造体の開始位置からピクセルのデータの開始位置までのバイト数が記録されています。つまり、sizeof( BITMAPFILEHEADER ) + sizeof( BITMAPINFOHEADER ) + ( sizeof( RGBQUAD ) * カラーテーブル数 )の値が記録されています。
この構造体に記録されている情報で興味があるのはbfTypeぐらいです。とりあえず読み込んだらファイルの種別を判別した方がいいでしょう。
BITMAPINFOHEADER構造体
BITMAPINFOHEADER構造体は以下の様な形式をしています。
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;
- biSizeにはsizeof( BITMAPINFOHEADER )の値が記録されています。
- biWidthには画像の横幅(ピクセル数)が記録されています。
- biHeightには画像の縦幅(ピクセル数)が記録されています。
- biPlanesには「ターゲットのデバイスに対する面の数」を記録するそうですが、必ず「1」が記録されています。
- biBitCountには1ピクセルあたりのビット数が記録されています。つまり1,4,8,24のいずれかです。
- biCompressionには画像の圧縮方法が記録されています。ビットマップ形式の場合は「BI_RGB(0Lとして定義されてる)」が記録されています。
- biSizeImageには画像のバイト数を記録することになっていますが、ビットマップ形式の場合は0が記録されています。
- biXPelsPerMeterには「ビットマップを表示する対象のデバイスの1メートルあたりのピクセル数」(要は解像度)を記録することになっていますが、ペイントで生成したファイルの場合は1が記録されています。
- biYPelsPerMeterもbiXPelsPerMeterと同じく1が記録されています。
- biClrUsedには記録されているカラーテーブルの内、実際に使用しているカラーテーブルの数が記録されています。0が指定されている場合は全てのカラーテーブルを使用します。というより、いつも0が記録されています。
- biClrImportantにはカラーテーブルの内「重要な」色のカラーテーブルの数が記録されています。0が記録されている場合は全ての色が重要であることを示します。というより、いつも、0が記録されています。
この構造体で興味がある情報はbiWidthとbiHeight・biBitCountです。
また、この構造体にはいろいろな種類があり、それぞれサイズが異なります。そのため、サポートするファイルの種類を増やしたい場合はこのサイズを見て次のデータの読み込む位置を決定してください。でも、現実的にはあまり気にしなくても何とかなります。
RGBQUAD構造体の配列
RGBQUAD構造体は以下のような構造をしています。
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
ほぼ説明不要な構造体です。ファイル中にこの構造体の配列が記録されています。記録されている数はBITMAPINFOHEADERのbiBitCountの値から求めます。
| biBitCount |
RGBQUADの数 |
| 1 |
2 |
| 4 |
16 |
| 8 |
256 |
| 24 |
0 |
biBitCountが24の場合はカラーテーブルはありません。それ以降に記録されているピクセルのデータがそのまま色を示しています。それ以外の場合は、読み込んだ「ピクセルのデータ」は、このカラーテーブルのインデックスとなっています。たとえばbiBitCountが8の時にピクセルデータとして0x01を読み込んだとしたら、カラーテーブルの1番目の色を参照します。
ピクセルのデータ
後は各ピクセルごとのデータを読み込むだけです。と言ってもこれが一番の曲者です。
各ピクセルごとのビット数
これは当然のことながらBITMAPINFOHEADERのbiBitCountの値です。もしこれが1とか4とかだった場合は1ビットごと・4ビットごとに読み込まなくてはなりません。
データの記録されている順番
ピクセルのデータはなぜか左下から右方向に向かって記録されています。つまりファイル中のピクセルデータの先頭は画像の左下であり、ファイル末尾に向かうにつれ画像を右に向かってゆき、右端に達したら1ピクセル上の行の左端に移動します。

パディング
もう、最悪です。1行は必ず4バイト単位で記録されています。そして、もしBITMAPINFOHEADERのbiWidthが4で割り切れない場合は右端にパディングがあります。

たとえば24ビットカラーで横に10ピクセルあったとした場合、1行が32バイトで記録されていて31バイト目と32バイト目は意味のないデータだと言うことです。また、これが8ビットカラーだった場合は1行が12バイトで末尾の2バイトはパディングになります。
24ビットカラーにおける注意点
24ビットカラーの場合はカラーテーブルは使いません。その代わりに各ピクセルごと・各色ごとの強さが直接記録されています。
この際、通常ならばRGBが8ビットずつ記録されていると思いたくなりますが、ビットマップ形式の場合はBGRで記録されています。色の順番が逆なので気をつけてください。
読み込んでみる
以上のことをふまえてビットマップ形式のファイルを読み込んでみたいと思います。
ここでは、ビットマップを読み込み、HTMLを出力するものとします。ビットマップの各ピクセルを1つのセルとしたテーブルに変換致します。データ量が壮絶なことになりますが。
BmpIoLic.cとBITest.cをコンパイル・リンクして、実行形式のファイルを生成してください。
なお、ビッグエンディアンな環境で実行したい場合には、「BmpIoLib.c」中で定義している「ISLITTLEENDIAN」に”0”を指定してください。現状ではリトルエンディアン用となっています。
|