noocyte のプログラミング研究室 〜プログラムは楽しげに走らねばならない♪〜

BMPファイルのフォーマット

公開:2006/09/21(木)
最終更新:2016/06/22(水)

0.目次

  1. BMPファイルの構造
  2. BITMAPFILEHEADER 構造体 (14バイト)
  3. ビットマップ情報ヘッダ
    1. BITMAPCOREHEADER 構造体 (12バイト)
    2. BITMAPINFOHEADER 構造体 (40バイト)
    3. BITMAPV4HEADER 構造体 (108バイト)
    4. BITMAPV5HEADER 構造体 (124バイト)
  4. カラーマスク/カラーテーブル
  5. ビットマップデータ
  6. プロファイルデータ
  7. 注意
  8. 参考コーディング例 (C/C++)
    1. ビットマップデータサイズ (走査線のバイト数および全バイト数) の計算
    2. ヘッダ情報の作成
  9. サイト内関連ページ
  10. 外部へのリンク
  11. 更新履歴

1.BMPファイルの構造


BITMAPFILEHEADER 構造体
BITMAPINFO ビットマップ情報ヘッダ
(下記のいずれかの構造体)
カラーマスク (BI_BITFIELDS の場合のみ)
カラーテーブル (1/4/8 BPP では必須,16/24/32 BPP ではオプション)
(ここに隙間がある場合もある.(ない場合は packed bitmap と呼ばれる.))
ビットマップデータ
プロファイルデータ (オプション)

●BMPファイル内の構造体に関する注意

2.BITMAPFILEHEADER 構造体 (14バイト)

#include <pshpack2.h>
typedef struct tagBITMAPFILEHEADER {
  WORD  bfType;
  DWORD bfSize;         /* DWORD (4バイト) 境界にアラインされていない点に注意.*/
  WORD  bfReserved1;
  WORD  bfReserved2;
  DWORD bfOffBits;      /* DWORD (4バイト) 境界にアラインされていない点に注意.*/
} BITMAPFILEHEADER;
#include <poppack.h>
bfType
BMP ファイルであることを示す2バイト値.第1バイトは 'B' (0x42),第2バイトは 'M' (0x4D). つまり,
bfType == 'B' | ('M' << 8)   // 0x4D42 (CPU がリトルエンディアンの場合)
bfType == ('B' << 8) | 'M'   // 0x424D (CPU がビッグエンディアンの場合)
bfSize
BMP ファイルのサイズ (バイト数).

0やデタラメな値が入っていることがあるので, 読むときにはこれをアテにしてはいけない. ファイル全体のサイズを知らなくても BMP ファイルは読める. そもそも,BMP ファイルを丸ごとメモリに読み込むというのはあまり良い方法ではない. BITMAPINFOビットマップデータの間に不要な隙間がある場合もあるし, BITMAPFILEHEADER は読込み後は不要である (場合によっては BITMAPINFO も).

bfReserved1,bfReserved2
予約されている.0でなければならない.
bfOffBits
この構造体の先頭 (つまり BMP ファイルの先頭) からビットマップデータまでのバイト・オフセット.

3.ビットマップ情報ヘッダ

3.1 BITMAPCOREHEADER 構造体 (12バイト)

typedef struct tagBITMAPCOREHEADER {
  DWORD bcSize;     /* used to get to color table */
  WORD  bcWidth;
  WORD  bcHeight;
  WORD  bcPlanes;
  WORD  bcBitCount;
} BITMAPCOREHEADER;

3.2 BITMAPINFOHEADER 構造体 (40バイト)

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
この構造体のサイズ (バイト数).
biWidth
ビットマップの幅 (画素数).
biCompression が BI_JPEG または BI_PNG の場合,biWidth は解凍された JPEG または PNG 画像の幅を表す.
biHeight
ビットマップの高さ (画素数).biHeight>0 ならばボトムアップ DIB であり, その原点は左下隅である.biHeight<0 ならばトップダウン DIB であり, 原点は左上隅である. トップダウン DIB の場合,biCompression は BI_RGB または BI_BITFIELDS でなければならず,圧縮はできない.
biCompression が BI_JPEG または BI_PNG の場合,biWidth は解凍された JPEG または PNG 画像の高さを表す.
biPlanes
ターゲット・デバイスのプレーンの枚数.1でなければならない.
biBitCount
1画素当たりのビット数 (Bits-Per-Pixel,BPP). 有効な値は表 3.2-1 を参照.
biCompression
ビットマップの圧縮方式を指定する.トップダウン DIB は圧縮できない. 有効な値は表 3.2-2 を参照.
biSizeImage
ビットマップデータのサイズ (バイト数). BI_RGB の場合は0でもよい.
BI_JPEG または BI_PNG の場合, biSizeImage はそれぞれ JPEG または PNG の画像バッファのサイズを示す.

0やデタラメな値が入っていることがあるので, これをアテにしてはいけない.後述するように BI_RGB や BI_BITFIELDS の場合,biSizeImage の正しい値は biWidth,biHeight,biBitCount から計算することができるので, この値を使う必要はない.

biXPelsPerMeter, biYPelsPerMeter
それぞれ,ターゲット・デバイスの水平および垂直解像度 (単位は画素数/m). アプリケーションはこれらの値を, リソースグループの中からデバイスに最適なビットマップを選択するために使用してもよい.
biClrUsed
ビットマップが実際に使用するカラーテーブルのエントリ数.
biClrUsed=0 の場合,ビットマップは biBitCount に対応する最大色数を使用する.
biClrUsed>0 かつ biBitCount<16 の場合,biClrUsed は実際に使用される色数.
biBitCount≧16 の場合,biClrUsed はシステムカラーパレットの性能を最適化するのに用いられるカラーテーブルのサイズ.
biBitCount=16 または 32 の場合,最適カラーパレットは3つの DWORD マスクの直後から始まる.
biClrImportant
ビットマップを表示するのに必要な色数 (≦ カラーテーブルのエントリ数).0ならばすべての色が必要.

どう使えばいいのかわからない.Windows SDK v6.1 のマニュアルを "biClrImportant" で検索してみても,biClrImportant を0または最大色数に設定している例しか見つからない. 作成するときは常に0を設定しておけばたぶん OK.


表 3.2-1 biBitCount (BPP) の有効な値
BPP 意味
0 実際の BPP は,JPEG または PNG フォーマットで明示または暗示される.
1 モノクローム・ビットマップであり, ビットマップデータの各ビットが画素を表す.バイト内の上位のビットが左. カラーテーブルは2つのエントリを持つ. 画素ビットが0ならば bmiColors[0] に,1ならば bmiColors[1] に対応する.
4 最大16色であり,カラーテーブルは最大16エントリを持つ. ビットマップデータの各ニブルが1画素を表す.バイト内の上位のニブルが左.
8 最大256色であり,カラーテーブルは最大256エントリを持つ. ビットマップデータの各バイトが1画素を表す.
16 最大 216 色.ビットマップデータの各 WORD が1画素を表す.
24 最大 224 色. ビットマップデータの連続する3バイトが1画素を表す.
32 最大 232 色. ビットマップデータの各 DWORD が1画素を表す.

表 3.2-2 biCompression の有効な値
定数名 説明
BI_RGB 0 非圧縮形式.
BI_RLE8 1 8 BPP 用ランレングス符号化 (RLE) 形式. この形式は,1バイトのカウント値の後に1バイトのカラーインデックスが続く2バイト形式である.
参考:Bitmapファイルフォーマット
BI_RLE4 2 4 BPP 用ランレングス符号化 (RLE) 形式.
参考:Bitmapファイルフォーマット
BI_BITFIELDS 3 非圧縮形式であり,カラーテーブルは R, G, B のカラーマスクを指定する3つのエントリからなる. この形式は 16 BPP と 32 BPP のときに限り有効.
BI_JPEG 4

それぞれ,JPEG/PNG 画像であることを示す.

注意:これらはBMPファイルで使用するための値ではない.
(そりゃそうだ.JPEG/PNG 画像を使いたければ JPEG/PNG ファイルにすればいいわけで, BMP ファイルに入れても無駄にヘッダサイズが増えるだけで何もうれしくない.)

これらの値は, SetDIBitsToDevice() や StretchDIBits() を用いて,JPEGやPNG画像をサポートするプリンタに JPEG/PNG データをそのまま出力 (丸投げ,JPEG/PNG pass-through) する場合に使用するためのもの.(Win32 API (GDI) がこれらの API に対して JPEG/PNG の圧縮・伸張をサポートするわけではない.)

参考

BI_PNG 5

3.3 BITMAPV4HEADER 構造体 (108バイト)

この構造体は BITMAPINFOHEADER 構造体の拡張版である.

Windows NT 3.51 以前:アプリケーションは BITMAPINFOHEADER を継続使用すべき.

アプリケーションは新たに追加された機能を使用するため BITMAPV5HEADER を使用してもよい.

typedef struct {
  DWORD        bV4Size;
  LONG         bV4Width;
  LONG         bV4Height;
  WORD         bV4Planes;
  WORD         bV4BitCount;
  DWORD        bV4V4Compression; /* V4 が2回繰り返しているのは WinGDI.h のミスタイプ? */
  DWORD        bV4SizeImage;
  LONG         bV4XPelsPerMeter;
  LONG         bV4YPelsPerMeter;
  DWORD        bV4ClrUsed;
  DWORD        bV4ClrImportant;
  DWORD        bV4RedMask;
  DWORD        bV4GreenMask;
  DWORD        bV4BlueMask;
  DWORD        bV4AlphaMask;
  DWORD        bV4CSType;
  CIEXYZTRIPLE bV4Endpoints;
  DWORD        bV4GammaRed;
  DWORD        bV4GammaGreen;
  DWORD        bV4GammaBlue;
} BITMAPV4HEADER, FAR *LPBITMAPV4HEADER, *PBITMAPV4HEADER;
bV4Size
この構造体のサイズ (バイト数).アプリケーションはこの値を用いて, どのビットマップ情報ヘッダ構造体が使われているのかを判断すべきである.
bV4Width
BITMAPINFOHEADER::biWidth と同様.
bV4Height
BITMAPINFOHEADER::biHeight と同様.
bV4Planes
BITMAPINFOHEADER::biPlanes と同様.
bV4BitCount
BITMAPINFOHEADER::biBitCount と同様.
bV4V4Compression
BITMAPINFOHEADER::biCompression と同様.
bV4SizeImage
BITMAPINFOHEADER::biSizeImage と同様.
bV4XPelsPerMeter, bV4YPelsPerMeter
BITMAPINFOHEADER::biXPelsPerMeterBITMAPINFOHEADER::biYPelsPerMeter と同様.
bV4ClrUsed
下記の点を除き,BITMAPINFOHEADER::biClrUsed と同様.
biBitCount=16 または 32 の場合,最適カラーパレットは BITMAPV4HEADER の直後から始まる.
bV4ClrImportant
BITMAPINFOHEADER::biClrImportant と同様.
bV4RedMask, bV4GreenMaskbV4BlueMask, bV4AlphaMask
bV4V4Compression=BI_BITFIELDS のときに限り有効. それぞれ,各画素の RGB,アルファ要素のカラーマスク.
bV4CSType
DIB の色空間を指定する.有効な値は下記のとおり.
定数名 説明
LCS_CALIBRATED_RGB 0x00000000 endpoints とガンマ値がそれぞれ bV4Endpoints と bV4Gamma{Red,Green,Blue} に指定されていることを示す.
LCS_sRGB 'sRGB'
(0x73524742)
sRGB
LCS_WINDOWS_COLOR_SPACE 'Win '
(0x57696E20)
システムのデフォルト色空間 (sRGB).
PROFILE_LINKED 'LINK'
(0x4C494E4B)
(V5 専用) bV5ProfileData はカラープロファイルのファイル名 (NUL 終端 Windows-1252 文字列) を指す.bV5Endpoints と bV5Gamma* は無視される.
PROFILE_EMBEDDED 'MBED'
(0x4D424544)
(V5 専用) bV5ProfileData は使用するプロファイルを含むメモリバッファを指す.
論理色空間を定義する情報については LOGCOLORSPACE 構造体を参照.
bV4Endpoints
の各 endpoint に対応する, 論理色空間の x,y,z 座標を指定する CIEXYZTRIPLE 構造体. bV4CSType=LCS_CALIBRATED_RGB のときに限り有効.

補足:マニュアルにはあまり説明がないが,ここでいう“論理色空間”は CIE 1931 XYZ 色空間を指すと考えられる.この空間の中でに対応する3つの endpoint (RGB の色域となる三角形の頂点) を指定することで,さまざまな RGB 色空間を定義することができる.また,マニュアルには "x, y, z" と小文字で書かれているが,"X, Y, Z" (大文字) が正しいはず (小文字の xyz 座標には輝度情報がほとんど含まれない).

bV4GammaRed, bV4GammaGreen, bV4GammaBlue
各色成分のガンマ値を 16.16 形式 (整数部16bit+小数部16bitの固定小数点数) で表す.bV4CSType=LCS_CALIBRATED_RGB のときに限り有効.

3.4 BITMAPV5HEADER 構造体 (124バイト)

typedef struct {
  DWORD        bV5Size;
  LONG         bV5Width;
  LONG         bV5Height;
  WORD         bV5Planes;
  WORD         bV5BitCount;
  DWORD        bV5Compression;
  DWORD        bV5SizeImage;
  LONG         bV5XPelsPerMeter;
  LONG         bV5YPelsPerMeter;
  DWORD        bV5ClrUsed;
  DWORD        bV5ClrImportant;
  DWORD        bV5RedMask;
  DWORD        bV5GreenMask;
  DWORD        bV5BlueMask;
  DWORD        bV5AlphaMask;
  DWORD        bV5CSType;
  CIEXYZTRIPLE bV5Endpoints;
  DWORD        bV5GammaRed;
  DWORD        bV5GammaGreen;
  DWORD        bV5GammaBlue;
  DWORD        bV5Intent;
  DWORD        bV5ProfileData;
  DWORD        bV5ProfileSize;
  DWORD        bV5Reserved;
} BITMAPV5HEADER, FAR *LPBITMAPV5HEADER, *PBITMAPV5HEADER;
bV5Size
BITMAPV4HEADER::bV4Size と同様.
bV5Width
BITMAPINFOHEADER::biWidth と同様.
bV5Height
BITMAPINFOHEADER::biHeight と同様.
bV5Planes
BITMAPINFOHEADER::biPlanes と同様.
bV5BitCount
BITMAPINFOHEADER::biBitCount と同様.
bV5Compression
BITMAPINFOHEADER::biCompression と同様.
bV5SizeImage
BITMAPINFOHEADER::biSizeImage と同様.
bV5XPelsPerMeter, bV5YPelsPerMeter
BITMAPINFOHEADER::biXPelsPerMeterBITMAPINFOHEADER::biYPelsPerMeter と同様.
bV5ClrUsed
BITMAPINFOHEADER::biClrUsed と同様.
bV5ClrImportant
BITMAPINFOHEADER::biClrImportant と同様.
bV5RedMask, bV5GreenMaskbV5BlueMask, bV5AlphaMask
それぞれ, BITMAPV4HEADER::bV4RedMask, BITMAPV4HEADER::bV4GreenMask, BITMAPV4HEADER::bV4BlueMask, BITMAPV4HEADER::bV4AlphaMask と同様.
bV5CSType
BITMAPV4HEADER::bV4CSType と同様.
bV5Endpoints
BITMAPV4HEADER::bV4Endpoints と同様.
bV5GammaRed, bV5GammaGreen, bV5GammaBlue
それぞれ, BITMAPV4HEADER::bV4GammaRed, BITMAPV4HEADER::bV4GammaGreen, BITMAPV4HEADER::bV4GammaBlue と同様.
bV5Intent
レンダリングインテント (rendering intent,描画意図,色空間の変換方法).可能な値は次のとおり.

意図 ICC名 意味
LCS_GM_ABS_COLORIMETRIC 一致
(Match)
絶対的な
色彩を維持
(Absolute Colorimetric)
変換先の色域 (color gamut) 内の最も近い色を採用する.画像の輝度を変更する可能性がある白点と黒点の調整を行わない.
LCS_GM_GRAPHICS 校正刷
(Proof)
相対的な
色彩を維持
(Relative Colorimetric)
変換結果が変換先の色域に収まる場合は何もしない.はみ出す場合には色域内の最も近い色に置き換える.このため,異なる色が同じ色に変換される場合がある.
LCS_GM_IMAGES 写真
(Picture)
知覚 (Perceptual) コントラストを維持する.写真や自然な画像向け.変換結果が変換先の色域に収まらない場合,変換結果全体の色域を縮小して変換先に収める.
LCS_GM_BUSINESS
(Graphic)
彩度 (Saturation) ビジネス・グラフィックスやディザなしの色が必要な場合向け.色の相対的彩度を維持する.

■参考

bV5ProfileData
BITMAPV5HEADER 構造体の先頭からプロファイルデータの先頭までのバイト・オフセット.プロファイルデータの本体は次のとおり.
  • bV5CSType=PROFILE_LINKED の場合
    プロファイルのファイル名 (NUL 終端文字列). Unicode 文字列は不可.Windows-1252 でないとダメ.(なんでやねん!)
  • bV5CSType=PROFILE_EMBEDDED の場合
    詳細は Using Structures in WCS 1.0 を参照 (ちょっと面倒そうなのでまだちゃんと読んでいない).
参考:Using Structures in WCS 1.0
bV5ProfileSize
埋め込まれたプロファイルデータのサイズ (バイト数).
bV5Reserved
将来のために予約されている.0でなければならない.

4.カラーマスク/カラーテーブル

BITMAPINFO::bmiColors は RGBQUAD[1] として宣言されているが,マニュアルを読む限り実際には次のような構造になる (はず).

struct {
#if biCompression == BI_BITFIELDS
  struct {
    DWORD red;
    DWORD green;
    DWORD blue;
  } colorMask;
#endif /* biCompression == BI_BITFIELDS */
  RGBQUAD colorTable[biClrUsed];
} bmiColors;

5.ビットマップデータ

BYTE bitmapData[<走査線の本数>][<走査線のバイト数>];

6.プロファイルデータ

ビットマップ情報ヘッダが BITMAPV5HEADER で, かつ bv5CSType が PROFILE_LINKED または PROFILE_EMBEDDED の場合にのみ存在する.


7.注意

7.1  4BPP以下の画像を複数 (2または4) バイト単位で処理する場合

4 BPP 以下の画像では,1バイトに複数の画素値が入っている. 走査線は4バイト境界にアラインされているので, 4バイト単位 (場合によっては2バイト単位) で処理することにより高速化を図ることも可能だが, 次の点に注意する必要がある.

1バイトの中では,走査線内の画素は上位ビットから下位ビットに向かって格納される. つまりバイト内での画素の格納順序はビッグエンディアンである. 1 BPP の場合,バイト内の各ビットは次のような配置で画像に表示される. 図中の番号はバイト内のビット位置を表す (LSB=0,MSB=7).

76543210

ビットマップデータを複数バイト単位で処理する場合,CPU のエンディアンが問題となる. ビットマップデータの4バイトを1つの4バイト整数として扱う場合, ビッグエンディアン CPU では整数内の各ビットは次のような配置で画像に表示される (LSB=0,MSB=31).

31302928 27262524
23222120 19181716
15141312 111098
76543210

一方,リトルエンディアン CPU では次のようになる.

76543210
15141312 111098
23222120 19181716
31302928 27262524

つまりリトルエンディアン CPU では, 2バイトまたは4バイト整数内のビット (画素) 順と, 画像上の画素順が一致していない. このためビットマップデータをバイト単位で処理していたのを, 高速化のためそのまま2バイトまたは4バイト単位で処理するように変更しようとしても, 画素の順序が狂ってしまってうまくいかない場合がある.
(私も最近やっちゃいました.(^^;))


8.参考コーディング例 (C/C++)

今までに何度か,他人様の書いた BMP ファイルを読み書きするコードを見る機会があったが, あまりにひどいものがいくつかあったので,ここに参考コーディング例を載せることにした. 「ひどい」の内容は次のとおり.

とりあえず,バグの原因になりやすい, ビットマップデータサイズの計算方法を中心に書くことにする.
(そのうち気が向いたら他の部分も書きます.)

8.1 ビットマップデータサイズ (走査線のバイト数および全バイト数) の計算

画像の幅を width,高さを height とする. まず,1本の走査線のサイズ (DWORD 数) lineSizeDW を次のようにして計算する. (8 BPP の場合はどちらの方法でもよい. また 2 BPP は BMP ファイルの仕様には含まれていないが, もし存在しても 1 BPP や 4 BPP の場合と同様に計算できる.)

次に,走査線のサイズ (バイト数) lineSize とビットマップデータの全バイト数 imageSize を求める.

DWORD lineSize = lineSizeDW * sizeof(DWORD);
DWORD imageSize = lineSize * height;

8.2 ヘッダ情報の作成

上で計算した lineSize,imageSize を用いて次のようにヘッダ情報を設定する.

BITMAPFILEHEADER bf;
BITMAPINFOHEADER bi;

// ヘッダサイズの合計
const DWORD headerSize = sizeof(bf) + sizeof(bi);

// カラーテーブルのエントリ数 nColors を計算する.
// RGB カラー (BPP>8) の場合はないものとする.
unsigned nColors = (BPP <= 8) ? (1U << BPP) : 0;

// カラーテーブルのサイズ (バイト数) を計算する.
DWORD colorTableSize = sizeof(RGBQUAD) * nColors;

bf.bfType = 'B' | ('M' << 8);
bf.bfReserved1 = 0;
bf.bfReserved2 = 0;
bf.bfOffBits = headerSize + colorTableSize;     // packed bitmap
bf.bfSize = bf.bfOffBits + imageSize;

bi.biSize = (DWORD)sizeof(bi);
bi.biWidth = (LONG)width;
bi.biHeight = (LONG)height;
bi.biPlanes = 1;
bi.biBitCount = (WORD)BPP;
bi.biCompression = BI_RGB;
bi.biSizeImage = imageSize;
bi.biXPelsPerMeter = 0;
bi.biYPelsPerMeter = 0;
bi.biClrUsed = nColors;
bi.biClrImportant = 0;
2007/08/12(日) 追記

以上はリトルエンディアン CPU 用なので,ビッグエンディアン CPU を使用してヘッダをファイルに書き出す場合は最後に bf および bi の全メンバのエンディアンを反転させること.


9.サイト内関連ページ


10.外部へのリンク

未整理,敬称略.


11.更新履歴

  1. 2006/09/21(木) 書きかけだがとりあえず公開.
  2. 2006/10/19(木) とりあえず外部へのリンクを追加.
  3. 2006/10/27(金) 注意:4BPP以下の画像を複数 (2または4) バイト単位で処理する場合を追加.
  4. 2007/01/20(土) BITMAPV4HEADER 構造体および BITMAPV5HEADER 構造体の各メンバの説明を追記中 (未完成). (でもまだ色管理用語については理解していない.(^^;))
  5. 2007/02/25(日) BITMAP*HEADER 構造体のサイズ (バイト数) を追記.
  6. 2007/03/04(日) 参考コーディング例 (C/C++) を追加.
  7. 2007/03/12(月)
  8. 2007/04/18(水) BMPファイルの構造図に,BITMAPINFO の後の隙間を追記.
  9. 2007/06/23(土) 外部へのリンクを追加・整理.
  10. 2007/07/29(日) bfTypebfSizebiSizeImage の説明に追記.
  11. 2007/08/12(日) ヘッダ情報の作成に, ビッグエンディアン CPU でヘッダを書き出す場合の注意書きを追記.
  12. 2008/06/26(木)「サイト内関連ページ」を忘れていたので追加.
  13. 2012/12/21(金)「BI_JPEG/BI_PNG」の説明を追記.
  14. 2014/07/31(木)「bV5Intent」の説明を追記.
  15. 2016/06/22(水) カラーマネジメント関連メンバの説明を追記.


Copyright © 2006-2016 noocyte
E-mail: relipmoced (a) yahoo.co.jp
  (" (a) " を半角のアットマークに書き替えてください.)
リンクはご自由に.
「noocyte のプログラミング研究室」トップページに戻る.


track feed noocyte