Windows BMP File Format

Windowsでよく使用されている画像ファイルフォーマットとしては、BMP 形式があります。本稿では、実際に、BMPファイルのデータを見ながら、BMP ファイルの構造を理解してみましょう。

Windows BMFフォーマットは、OS/2形式や、バージョンによる違いなどがあり、結構真剣にサポートしようとするといろいろと大変なようです。ここでは、Windows XP Home Edition 付属のペイントで作成したBMPファイルを対象にして解析をしてみました。読者の環境によってはフォーマットが微妙に異なる場合もあると思いますが、基本をおさえておけば対応できるはずです。

まず、データを表示するためにプログラミング言語Cで記述した簡単なダンププログラムをここに示します。世の中で出回っているライブラリやツールを使ってもいいのですが、BMP ファイルのヘッダを解析してRAW データを自分で取り出そうと思っている場合は、こうやって自作できるようになっておくことも必要なので、紹介しておきます。動作確認は、Linux で行っています。特別な関数は使っていないので、Windows のコンソールプロンプトでも動くはずです。

#include <stdio.h>
#include <stdlib.h>

void dump(FILE* in);

int main(int argc, char* argv[]) {
  FILE* inputFile;
  char* ifile;

  if (argc < 2) {
    printf("USAGE: dump <file>\n");
    exit(1);
  }
  ifile = argv[1];

  if (NULL == (inputFile=fopen(ifile, "rb"))) {
    printf("\n Can't open read file: %s\n", ifile);
    exit(1);
  }
  printf("-- dump data : %s --\n", ifile);
  printf("----+-------------------------------------------------\n");
  printf("    | 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 \n");
  printf("----+-------------------------------------------------\n");
  dump(inputFile);
  if (fclose(inputFile)) {
    printf("\n Can't close read file: %s\n", ifile);
    exit(1);
  }
  return 0;
}

void dump(FILE* in) {
  int c = 0;
  int line = 0;
  printf("%04x:", line);
  while ((c=fgetc(in)) != EOF) {
    printf(" %02X", c);
    line++;
    if (line % 16 == 0) {
      printf("\n%04x:", line);
    }
  }
  printf("\n");
}

このプログラムでは、指定したファイルが保存しているデータ値を1byte単位で読み込み、それを16進数で表示しています。何個目の文字かがわかりやすいように、左端には16進数で文字数を表示し、16文字ごとに改行するようにしています。

上記ファイルを dump.c と名前をつけて保存し、コンパイルします。gcc を使う場合は、次のようにして、dumpコマンドを作成します。
$ gcc -o dump dump.c

Windows BMPファイルのフォーマットを確認するために、まずは赤1pixel, 緑1pixel, 青1pixelの、3×1 (横長)のBMPファイルを用意しましょう。Windowsに標準で添付されているペイントを使って作成すると、普通は色数24bitカラーのBMPファイルが作成できます。ここで作成したBMPファイルに対して上記のツールを適用すると、次のような結果になるはずです。

— dump data : rgb.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 42 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 03 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 0C 00 00 00 12 0B 00 00 12 0B 00 00 00 00
0030| 00 00 00 00 00 00 00 00 FF 00 FF 00 FF 00 00 00
0040| 00 00

ここで表示されたバイナリデータとWindows BMPファイルのフォーマットを仕様と照らし合わせて具体的に見てみると、次の通りになります。Intel 80×86系CPUはリトルエンディアンなので、対応をとるときに注意が必要です。なお、ここでは、WORDは2bytes、DWORDは2×2=4bytes、LONGは4bytesのサイズを使うデータ型としています。16進数でバイナリデータが表示されることも忘れないようにしましょう。

WORD Type : 42 4D … ASCIIコードで”MB”(4D42)
DWORD Size : 42 00 00 00 … ファイルサイズ 66(00000042) bytes
WORD Reserved1 : 00 00 … reserveされている 0
WORD Reserved2 : 00 00 … reserveされている 0
DWORD OffBits : 36 00 00 00 … イメージのオフセット 54(00000036) byte目

DWORD Size : 28 00 00 00 … ヘッダサイズ 40(00000028) bytesの場合はWindows DIB
LONG Width : 03 00 00 00 … イメージの幅 3 pixels
LONG Height : 01 00 00 00 … イメージの高さ 1 pixel
WORD Planes : 01 00 … イメージプレーン数 1 (0001)
WORD BitCount : 18 00 … pixelあたりのbit数 24(0018) bits
DWORD Compression : 00 00 00 00 … 圧縮形式
DWORD SizeImage : 0C 00 00 00 … イメージのサイズ 12 (0000000C) bytes
LONG XPelsPerMeter: 12 0B 00 00 … 水平解像度 2834(00000B12)
LONG YPelsPerMeter: 12 0B 00 00 … 垂直解像度 2834(00000B12)
DWORD ClrUsed : 00 00 00 00 … 使用するカラー
DWORD ClrImportant : 00 00 00 00 …「重要」な色の数

Image data : 00 00 FF 00 FF 00 FF 00 00 00 00 00
… イメージデータ 赤(FF0000)緑(00FF00)青(0000FF)+パディング(000000)
*イメージデータは、4byteで割り切れるようにパディングされ調整されるようです。

1つのデータだけでは不安なので、他のBMPファイルに対しての結果も見てみましょう。1×1 pixelで、黒、白、赤、緑、青、で塗りつぶしたBMPファイルを作成して同様にバイナリデータを表示してみます。すると、それぞれに対して、次のような結果になるはずです。1×1 pixelの場合は、パディングのために 1byte余計なイメージデータが追加されています。

— dump data : black.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 3A 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 01 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
0030| 00 00 00 00 00 00 00 00 00 00

— dump data : white.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 3A 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 01 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 04 00 00 00 12 0B 00 00 12 0B 00 00 00 00
0030| 00 00 00 00 00 00 FF FF FF 00

— dump data : red.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 3A 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 01 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
0030| 00 00 00 00 00 00 00 00 FF 00

— dump data : green.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 3A 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 01 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
0030| 00 00 00 00 00 00 00 FF 00 00

— dump data : blue.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 3A 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 01 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00
0030| 00 00 00 00 00 00 FF 00 00 00

次に、3×1 pixels(横長)で、黒、白、赤、緑、青、で塗りつぶしたBMPファイルを用意しましょう。ここで作成したBMPファイルに対して同様に適用すると、次のような結果になるはずです。3×1 pixcelsの場合は、パディングのために 3byte余計なイメージデータが追加されているのがわかります。

— dump data : black3.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 42 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 03 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00
0030| 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0040| 00 00

— dump data : white3.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 42 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 03 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 0C 00 00 00 12 0B 00 00 12 0B 00 00 00 00
0030| 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF 00
0040| 00 00

— dump data : red3.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 42 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 03 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00
0030| 00 00 00 00 00 00 00 00 FF 00 00 FF 00 00 FF 00
0040| 00 00

— dump data : green3.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 42 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 03 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00
0030| 00 00 00 00 00 00 00 FF 00 00 FF 00 00 FF 00 00
0040| 00 00

— dump data : blue3.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 42 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 03 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 0C 00 00 00 00 00 00 00 00 00 00 00 00 00
0030| 00 00 00 00 00 00 FF 00 00 FF 00 00 FF 00 00 00
0040| 00 00

イメージデータの並び順も確認するために、RGBの16進数値がそれぞれ FF0000,660000,CCAA88という値のデータが順に並んでいる 3×1 pixelsのBMPファイルも作成して見てみましょう。左から順にデータがはいっているのがわかります。

— dump data : rgb2.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 42 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 03 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 0C 00 00 00 EB 0A 00 00 EB 0A 00 00 00 00
0030| 00 00 00 00 00 00 00 00 FF 00 00 66 88 AA CC 00
0040| 00 00

FF0000,660000,CCAA88
FF0000,660000,CCAA88
という 3×2 pixels のBMPファイルも見てみましょう。スキャンラインの単位でパディングがされる様子がわかります。

— dump data : rgb3.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 4E 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 03 00 00 00 02 00 00 00 01 00 18 00 00 00
0020| 00 00 18 00 00 00 EB 0A 00 00 EB 0A 00 00 00 00
0030| 00 00 00 00 00 00 00 00 FF 00 00 66 88 AA CC 00
0040| 00 00 00 00 FF 00 00 66 88 AA CC 00 00 00

もう少しパディングについて確認するために、FF0000,D90000,660000,B28066の4×1 pixelsのBMPファイルを作成して見てみましょう。ここでは、パディングが必要ないので、余計なイメージデータは追加されていません。

— dump data : rgb4.bmp —
—-+————————————————-
| 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10
—-+————————————————-
0000| 42 4D 42 00 00 00 00 00 00 00 36 00 00 00 28 00
0010| 00 00 04 00 00 00 01 00 00 00 01 00 18 00 00 00
0020| 00 00 0C 00 00 00 EB 0A 00 00 EB 0A 00 00 00 00
0030| 00 00 00 00 00 00 00 00 FF 00 00 D9 00 00 66 66
0040| 80 B2

どうでしたでしょうか。ファイルフォーマットを覗いてみると意外と面白いことがわかります。ヘッダー情報を有効に利用して、RAWデータを保存しているのがわかったのではないでしょうか。駆け足の説明でわかりにくいところが結構あると思いますが、何かありましたらコメントをいただければと思います。

[補足]
それほどBMPフォーマットに詳しいわけではないので,間違えているかもしれませんので,そのつもりで読んでください.
ここで紹介した Windows BMP ファイルの仕様によれば,BitCountには2バイト割り当てられているので,理論上は色調を高めることは可能です.24bit使うのを36bit使うようにすればいいだけですから.ただ,色調を高めることが高解像度,高画質に対応することに直結するかどうかは別だと思います.

同じカテゴリの記事: etc