ファイル仕様

BMP(ビットマップ)のファイルフォーマット

更新日:2023/03/17

ビットマップファイルはJPGやPNGよりも古い歴史を持つ画像ファイルフォーマットです。
同じ画像を出力するとJPEGやPNGの方がサイズが小さくなることから、今ではほとんど使用されていません。

そんな現状のため需要がほぼゼロだと思いますが、ビットマップファイルのファイルフォーマットを資料としてまとめておきます。

 

参考資料

この記事は、次のマイクロソフトの資料を参考にしています。

このページはBMPファイルのフォーマットについて触れていますが、他のページはC/C++でのプログラミング向けに書かれたものです。
そのため、BMPの各要素は構造体で説明されています。

この記事では、構造体名とメンバー名をそのまま使っています。

なお、構造体についてはWindows SDKに含まれるWinGDI.hというファイルで定義されています。

SDKがインストールされているなら、次のディレクトリに格納されているので探してみてください。
C:\Program Files (x86)\Microsoft SDKs\Windows\vX.X\Include

vX.Xは、SDKのバージョンです。
複数存在することもありますが、Includeディレクトリがないものあります。

なお、ビットマップはOS/2というwindowsとは異なるOSでも仕様定義されてきた過去があります。
こちらの仕様は、マイクロソフトの資料には含まれていません。
今ではOS/2を目にすることがないので、今回は省きます。

 

BMP固有の知識

BMPファイルのフォーマットをお伝えするにあたり、必要な前提知識を記述しておきます。

BMPファイルのエンディアン

BMPファイルの各値は、リトルエンディアン(LE)で格納します。

1バイトはそのままですが、2バイトはバイトが入れ替わります。

2バイト: 0x123434 12 でファイルに格納
4バイト: 0x1234567878 56 34 12 でファイルに格納

エンディアンについては、次のページを読んでみてください。
エンディアンとは何か調べてみた

ボトムアップとトップダウン

BMP内の画像データは、通常はボトムアップで格納されています。

ボトムアップは、画像の左下を座標(0,0)として右方向へピクセルの情報を格納します。
右端まで処理したら、一つ上の行を処理します。

ボトムアップ

トップダウンは左上が座標(0,0)です。
右端まで処理したら、下方向へ移動します。

トップダウン

画像は左上を原点にするイメージがあるので、左下が原点のボトムアップは上下が反転していることになります。
トップダウンの方がイメージ的に処理しやすいですが、RLE圧縮などの一部の機能はボトムアップに限定されます。

何らかの理由がないなら、ボトムアップで処理しておいた方がよさそうです。

4バイト境界

BMP内の画像データは、一行ごとに4バイトで割り切れるように調整する必要があります。

例えば幅111ピクセルの画像を1ピクセル3バイトで表現する場合、1行は3×111=333バイトで表すことができます。
これを4で割った余りを求めると、1です。
4で割り切れるようにするには、余分に3バイト出力して336バイトにする必要があります。

JPEGとPNG

BMPファイルはJPEGまたはPNGファイルをそのまま内部に格納することができます。
しかし、元となるファイルにBMPのヘッダを付加することになるので、ファイルサイズが増えるだけです。
あまり意味がないですね。

また、JPEG/PNGを格納したBMPファイルに対応していないアプリもあります。
BMPファイルを自作して確認してみたところ、Google Chromeでは読み込めましたが、FireFoxやWindowsのペイント3Dでは読み込めませんでした。

確認したブラウザ(2022/7時点での最新バージョン)
Google Chrome103.0.5060.134
FireFox102.0.1

RLE圧縮

BMPファイル内の画像データは、1ピクセルのビット数が4または8でボトムアップで処理されているとき、RLE圧縮できます。

RLE圧縮については、次のページを読んでみてください。
【JavaScript】 ランレングス圧縮(RLE、連長圧縮)とは

BMP固有の仕様は、圧縮形式:BI_RLE8(1)圧縮形式:BI_RLE4(2)を見てください。

DIBとビットマップ

BMPはビットマップデータをファイルに格納する方式の一つで、本来はビットマップそのものを表すものではないようです。
広い意味ではJPEGやPNGも、ビットマップに含まれるようです。

いろいろと言葉の意味が錯綜していますが、Windowsが取り扱っているビットマップ画像のファイルフォーマットをDIB(Device Independent Bitmap)、日本語でデバイス独立ビットマップと呼びます。
そしてこのファイルフォーマットで作成された画像データは、拡張子をbmpとします。

ここから、BMPイコール、ビットマップとなったようです。

 

BMPの構造

BMPの大まかな構造は、次のようになっています。

構造構造体名備考
ファイルヘッダーBITMAPFILEHEADER
情報ヘッダーBITMAPINFOHEADER(INFOヘッダー)
BITMAPV4HEADER(V4ヘッダー)
BITMAPV5HEADER(V5ヘッダー)
BITMAPCOREHEADER(COREヘッダー)
どれか一つ
カラーマスク4バイト×3存在しない可能性あり
パレットデータRGBQUAD/RGBTRIPLE存在しない可能性あり
カラーインデックスデータ
(画像データ)
パレットデータのインデックス番号
パレットデータのインデックス番号(RLE圧縮)
赤緑青(16bit)
赤緑青(3byte)
0赤緑青(4byte)
JPEG
PNG
どれか一つ

情報ヘッダーの構造体名の()は、構造体名の差異がわかり難いので便宜上省略したものです。

 

ファイルヘッダー

BMPファイルは、ファイルヘッダーからスタートします。

BITMAPFILEHEADER

総バイト数14

名前バイト数内容
bfType2"BM"固定
bfSize4ファイルのバイト数
bfReserved120固定
bfReserved220固定
bfOffBits4カラーインデックスデータへのオフセット

"BM"は、バイト値で 0x42 と 0x4d です。
これはエンディアンを気にせず、最初のバイトを0x42にします。

bfOffBitsは、ファイルの先頭を0として、カラーインデックスデータ(画像データ)が開始するバイト位置です。

 

情報ヘッダー

マイクロソフトの資料によると、情報ヘッダーは4種類あります。
実際には他にもあるようですが、現在は使用されていないと思われます。

情報ヘッダーはCOREヘッダー → INFOヘッダー → V4ヘッダー → V5ヘッダー の順で情報量が増えて、より詳細な情報を設定できるようになります。

Windowsのペイント3Dは、INFOヘッダーでビットマップファイルを作成しています。
幾つかのアプリで確認してみたところ、同じようにINFOヘッダーが使用されていました。

自分でビットマップファイルを作成する場合は、INFOヘッダーでいい気がします。

情報ヘッダーの種類判定は、最初の4バイトの値で判断します。
最初の4バイトは、ヘッダーのバイト数を表します。

情報ヘッダー最初の4バイトの値
INFOヘッダー40
COREヘッダー12
V4ヘッダー108
V5ヘッダー124

BITMAPINFOHEADER(INFOヘッダー)

名前バイト数内容備考
biSize4情報ヘッダーのバイト数40固定
biWidth4画像の幅
biHeight4画像の高さ正値:ボトムアップ
負値:トップダウン
biPlanes2プレーン数1固定
biBitCount21ピクセルのビット数0:JPEGやPNG
1:1bitインデックス
4:4bitインデックス
8:8bitインデックス
16:RGB(各5bit) または ビットフィールド
24:RGBTRIPLE
32:RGBQUAD または ビットフィールド
biCompression4圧縮形式BI_RGB(0):無圧縮
BI_RLE8(1):8bitでRLE圧縮
BI_RLE4(2):4bitでRLE圧縮
BI_BITFIELDS(3):無圧縮&カラーマスク有
BI_JPEG(4):JPEGデータ
BI_PNG(5):PNGデータ
biSizeImage4画像データのサイズbiCompressionがBI_RGBのとき
0にできる。

biXPelsPerMeter4横方向の解像度ピクセル/メートル
biYPelsPerMeter4縦方向の解像度
biClrUsed4パレットデータの数この値が0でbiBitCountが1ならパレットデータは 2つ、4なら16、8なら256必要。
biClrImportant4画像表示に必要な色数0なら全て
2023/3/17 ご指摘により内容を訂正しました。
この値が0でbiCompressionが1なら → この値が0でbiBitCountが1なら

ご指摘ありがとうございます!

BITMAPCOREHEADER(COREヘッダー)

COREヘッダはOS/2で定義された情報ヘッダーですが、資料に掲載されているので取り上げます。

名前バイト数内容備考
bcSize4情報ヘッダーのバイト数12固定
bcWidth2画像の幅
bcHeight2画像の高さ正値:ボトムアップ
負値:トップダウン
bcPlanes2プレーン数1固定
bcBitCount21ピクセルのビット数1:1bitインデックス
4:4bitインデックス
8:8bitインデックス
24:RGBTRIPLE

1ピクセルのビット数は、1,4,8,24のみです。

BITMAPV4HEADER(V4ヘッダー)

名前バイト数内容備考
bV4Size4情報ヘッダーのバイト数108固定
bV4Width4画像の幅
bV4Height4画像の高さ正値:ボトムアップ
負値:トップダウン
bV4Planes2プレーン数1固定
bV4BitCount21ピクセルのビット数0:JPEGやPNG
1:1bitインデックス
4:4bitインデックス
8:8bitインデックス
16:RGB(各5bit) または ビットフィールド
24:RGBTRIPLE
32:RGBQUAD または ビットフィールド
bV4V4Compression4圧縮形式BI_RGB(0):無圧縮
BI_RLE8(1):8bitでRLE圧縮
BI_RLE4(2):4bitでRLE圧縮
BI_BITFIELDS(3):無圧縮&カラーマスク有
BI_JPEG(4):JPEGデータ
BI_PNG(5):PNGデータ
bV4SizeImage4画像データのサイズbV4V4CompressionがBI_RGBのとき
0にできる。
bV4XPelsPerMeter4横方向の解像度ピクセル/メートル
bV4YPelsPerMeter4縦方向の解像度
bV4ClrUsed4カラーパレットの数この値が0でbV4BitCountが1なら パレットデータは2つ、4なら16、8なら256必要。
bV4ClrImportant4画像表示に必要な色数0なら全て
bV4RedMask4赤のカラーマスクbV4V4Compressionが
BI_BITFIELDSのとき有効
bV4GreenMask4緑のカラーマスク
bV4BlueMask4青のカラーマスク
bV4AlphaMask4αのカラーマスク
bV4CSType4色空間LCS_CALIBRATED_RGB(0):
bV4Endpointsと3つの
ガンマ値が有効
bV4Endpoints36CIE XYZ色空間の定義bV4CSTypeが0のとき有効

名前バイト数内容
ciexyzRed12x,y,x各4バイト
ciexyzGreen12x,y,x各4バイト
ciexyzBlue12x,y,x各4バイト
bV4GammaRed4赤成分のガンマ値bV4CSTypeが0のとき有効
bV4GammaGreen4緑成分のガンマ値
bV4GammaBlue4青成分のガンマ値

bV4V4CompressionはV4が繰り返されていますね。
タイプミスのまま実装されてしまったのかもしれません。

V4ヘッダーはヘッダー内にカラーマスクが含まれています。
そのため、情報ヘッダーの後にマスクデータが設置されることはありません。

また、αのカラーマスクが追加されているので、bV4V4Compressionの値をBI_BITFIELDSにすることで透過ビットマップを生成できます。

ガンマ値は、 上位16ビットを整数部分とし、 下位16ビットを小数部分として指定します。

BITMAPV5HEADER(V5ヘッダー)

名前バイト数内容備考
bV5Size4情報ヘッダーのバイト数124固定
bV5Width4画像の幅
bV5Height4画像の高さ正値:ボトムアップ
負値:トップダウン
bV5Planes2プレーン数1固定
bV5BitCount21ピクセルのビット数0:JPEGやPNG
1:1bitインデックス
4:4bitインデックス
8:8bitインデックス
16:RGB(各5bit) または ビットフィールド
24:RGBTRIPLE
32:RGBQUAD または ビットフィールド
bV5Compression4圧縮形式BI_RGB(0):無圧縮
BI_RLE8(1):8bitでRLE圧縮
BI_RLE4(2):4bitでRLE圧縮
BI_BITFIELDS(3):無圧縮&カラーマスク有
BI_JPEG(4):JPEGデータ
BI_PNG(5):PNGデータ
bV5SizeImage4画像データのサイズbV5CompressionがBI_RGBのとき0にできる。
bV5XPelsPerMeter4横方向の解像度ピクセル/メートル
bV5YPelsPerMeter4縦方向の解像度
bV5ClrUsed4カラーパレットの数この値が0でbV5BitCountが1なら パレットデータは2つ、4なら16、8なら256必要。
bV5ClrImportant4画像表示に必要な色数0なら全て
bV5RedMask4赤のカラーマスクbV5CompressionがBI_BITFIELDSのとき有効
bV5GreenMask4緑のカラーマスク
bV5BlueMask4青のカラーマスク
bV5AlphaMask4αのカラーマスク
bV5CSType4色空間LCS_CALIBRATED_RGB(0):
LCS_sRGB('sRGB' : 0x73524742)
LCS_WINDOWS_COLOR_SPACE('Win ' : 0x57696e20)
PROFILE_LINKED('LINK' : 0x4c494e4b)
PROFILE_EMBEDDED('MBED' : 0x4d424544)
bV5Endpoints36bV5CSTypeが0のとき有効

名前バイト数内容
ciexyzRed12x,y,x各4バイト
ciexyzGreen12x,y,x各4バイト
ciexyzBlue12x,y,x各4バイト
bV5GammaRed4赤成分のガンマ値bV5CSTypeが0のとき有効
bV5GammaGreen4緑成分のガンマ値
bV5GammaBlue4青成分のガンマ値
bV5Intent4レンダリングインテントLCS_GM_BUSINESS(1):彩度
LCS_GM_GRAPHICS(2):相対カラーメトリック
LCS_GM_IMAGES(4):知覚
LCS_GM_ABS_COLORIMETRIC(8):絶対カラーメトリック
bV5ProfileData4V5ヘッダーの先頭からプロファイルデータへのオフセットbV5CSTypeがPROFILE_LINKED
またはPROFILE_EMBEDDED
のとき有効
bV5ProfileSize4プロファイルデータのサイズ
bV5Reserved4予約0固定

プロファイルデータはファイル名を指定します。
Windows文字セット(コードページ1252)を使用し、nullで終わる必要があります。

 

カラーマスク

カラーマスクは、次の条件に当てはまるとき、存在します。

  • 情報ヘッダーの種類がINFOヘッダー
  • 情報ヘッダーのbcBitCountが、16か32
  • 情報ヘッダーのbiCompressionが、BI_BITFIELDS
  • V4ヘッダーV5ヘッダーは、カラーマスクが情報ヘッダー内に含まれています。

    カラーマスクは3つの4バイトデータで構成されます。
    順番に赤、緑、青を受け持ちます。

    このときカラーインデックスデータ(画像データ)は4バイトのビットフィールドとして扱われ、各カラーマスクと論理積(AND)演算されます。

    例えばカラーマスクとして、赤:0x000000FF、緑:0x00FF0000、青:0x0000FF00が指定されているとします。
    このとき、ビットフィールドが0x00112233なら、赤33、緑11、青22と判断されます。

    ■補足

    32bit時のデフォルトのマスクは、次のようになります。

    マスク値
    0x00FF0000
    0x0000FF00
    0x000000FF

    16bitは各色5bit単位なので、デフォルトのマスクは次のようになります。

    マスク値
    0x00007C00
    0x000003E0
    0x0000001F

    ■補足

    各カラーマスクは、ビットが連続していることと、他のマスクとビットが重複しない必要があります。

    ▶NGパターン1: 連続していない

    例:
    0001111000111000

    ▶NGパターン2: 重複している

    例:
    0001111000000000
    0000001111000000

 

パレットデータ

パレットデータは、情報ヘッダーのパレットデータの数が1以上の時存在します。

ただし、1ピクセルのビット数が8以下でパレットデータの数が0のときは、それぞれのビットで表現できる最大数のパレットデータが必要です。

ビット数最大パレット数
12
416
8256

1ピクセルのビット数が16以上のときは、画像データはパレットのインデックスではなくて、RGB値です。
そのためパレットデータは特に意味を持ちませんが、アプリケーションによっては何らかの指標として使用している可能性があります。

パレットデータは次の形式で構成されます。

■COREヘッダーの場合

COREヘッダーのパレットデータは、RGBTRIPLEという形式です。
3バイトで一つのパレットデータを構成します。

バイト数
1
1
1

■COREヘッダー以外の場合

COREヘッダー以外のパレットデータは、RGBQUADという形式です。
4バイトで一つのパレットデータを構成します。

バイト数
1
1
1
10

この順番は、RGBを4バイトに割り振って、リトルエンディアン(LE)で格納したときと一致します。

0x00RRGGBB
↓ LEでファイル出力
BB GG RR 00

4バイト目は透明度を意味するような気がしますが、実際は切り捨てられます。

 

カラーインデックスデータ (画像データ)

カラーインデックスデータは、画像データです。
一つのデータで1ピクセルを表しますが、様々な方法で表現されます。

情報ヘッダーの1ピクセルのビット数と、圧縮形式で形式が決定されます。

また、情報ヘッダーの画像の高さが正のときはボトムアップで、負のときはトップダウンで処理されます。
詳しくは、この記事のボトムアップとトップダウンを見てください。

圧縮形式:BI_RGB(0)

BI_RGBの値は0です。

圧縮形式BI_RGBのときは、画像データは圧縮されていないことを意味します。

1ピクセルのビット数:1

1ピクセルのビット数が1のときは、パレットのインデックスを上位ビットから順番に1ビット単位でセットしていきます。
1バイトにつき、8つのピクセルを表現できます。

残ったビットは0をセットします。

1ピクセルのビット数:1

画像の各行は、バイト数を4の倍数に合わせる必要があります。

1ピクセルのビット数:4

1ピクセルのビット数が4のときは、パレットのインデックスを上位ビットから順番に4ビット単位でセットしていきます。
1バイトにつき、2つのピクセルを表現できます。

残ったビットは0をセットします。

画像の各行は、バイト数を4の倍数に合わせる必要があります。

1ピクセルのビット数:8

1ピクセルのビット数が8のときは、パレットのインデックスを上位バイトから順番に1バイト単位でセットしていきます。
1バイトにつき、1つのピクセルを表現します。

画像の各行は、バイト数を4の倍数に合わせる必要があります。

1ピクセルのビット数:16

1ピクセルのビット数が16のときは、2バイトで1つのピクセルを表現します。

上位ビットが0で、その後5ビット単位で赤、緑、青をセットします。

1ピクセルのビット数:16

画像の各行は、バイト数を4の倍数に合わせる必要があります。

1ピクセルのビット数:24

1ピクセルのビット数が24のときは、3バイトで1つのピクセルを表現します。

RGBTRIPLE形式です。
上位バイトが青で、その後、緑、赤をセットします。

画像の各行は、バイト数を4の倍数に合わせる必要があります。

1ピクセルのビット数:32

1ピクセルのビット数が32のときは、4バイトで1つのピクセルを表現します。

RGBQUAD形式です。
上位バイトが青で、その後、緑、赤をセットします。
4つ目のバイトは0です。

この形式は、自然に4の倍数になりますね。

圧縮形式:BI_RLE8(1)

BI_RLE8の値は1です。

圧縮形式BI_RLE8のときは、画像はボトムアップのみで処理されます。
1ピクセルのビット数が8である必要があります。

1ピクセルのビット数:8

8ビットのインデックスデータを、次の形式でRLE圧縮します。

データ種類データ
連続データ連続数(1バイト) データ(1バイト)
非連続データ 0(1バイト) データ数(1バイト) データ(1バイト)×データ数
行の終わり0(1バイト) 0(1バイト)
データの終わり0(1バイト) 1(1バイト)
右上移動0(1バイト) 2(1バイト) 右移動数(1バイト) 上移動数(1バイト)

非連続データのデータ数は、3以上を指定します。
2以下のときは、連続データで対応します。
また、非連続データのデータ部は必ず2で割り切れるように補完します。
例えば、データ数が3のときは、データが4バイトになります。

例:
05 0A 00 03 FF FF FF 00 00 00 01 DD 01 D0 06 FF 00 01
↓ デコード
0A 0A 0A 0A 0A FF FF FF
DD D0 FF FF FF FF FF FF

圧縮形式:BI_RLE4(2)

BI_RLE4の値は2です。

圧縮形式BI_RLE4のときは、画像はボトムアップのみで処理されます。
1ピクセルのビット数が4である必要があります。

1ピクセルのビット数:4

4ビットのインデックスデータを、次の形式でRLE圧縮します。

データ種類データ
連続データ連続数(1バイト) データ(4ビット) データ(4ビット)
非連続データ 0(1バイト) データ数(1バイト) データ(4ビット)×データ数
行の終わり0(1バイト) 0(1バイト)
データの終わり0(1バイト) 1(1バイト)
右上移動0(1バイト) 2(1バイト) 右移動数(1バイト) 上移動数(1バイト)

連続データは、二つの値を交互に繰り返します。
例えば 03 14 なら、1 4 1 に展開されます。

非連続データのデータ数は、3以上を指定します。
2以下のときは、連続データで対応します。
また、非連続データのデータ部は必ず2バイト単位になるように補完します。
例えば、データ数が5のときは、データは3バイトになります。

例:
05 14 00 03 FA C0 00 00 02 A1 01 D0 05 FF 00 01
↓ デコード
14 14 1F AC
A1 DF FF FF

圧縮形式:BI_BITFIELDS(3)

BI_BITFIELDSの値は3です。

圧縮形式BI_BITFIELDSのときは、画像データをビットフィールドとして扱います。
1ピクセルのビット数が16か32である必要があります。

INFOヘッダーは、カラーマスクが必要です。
V4、V5ヘッダーはヘッダー内にカラーマスクが含まれています。
また、V4、V5ヘッダーはα成分のマスクを指定できるため、ビットフィールド内に透明度を含むことができます。

1ピクセルのビット数:16

カラーマスクに従って、16ビット内に赤・緑・青を指定します。

1ピクセルのビット数:32

カラーマスクに従って、32ビット内に赤・緑・青を指定します。

圧縮形式:BI_JPEG(4)

BI_JPEGの値は4です。

圧縮形式BI_JPEGのときは、JPEG形式で画像データが保存されます。

1ピクセルのビット数:0

JPEGデータ内で、ビット数が指定されます。

圧縮形式:BI_PNG(5)

BI_PNGの値は5です。

圧縮形式BI_PNGのときは、PNG形式で画像データが保存されます。

1ピクセルのビット数:0

PNGデータ内で、ビット数が指定されます。

更新日:2023/03/17

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。