| BITMAPFILEHEADER 構造体 | |
| BITMAPINFO | ビットマップ情報ヘッダ (下記のいずれかの構造体)
|
| カラーマスク (BI_BITFIELDS の場合のみ) | |
| カラーテーブル (1/4/8 BPP では必須,16/24/32 BPP ではオプション) | |
| (ここに隙間がある場合もある.(ない場合は packed bitmap と呼ばれる.)) | |
| ビットマップデータ | |
| プロファイルデータ (オプション) | |
#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 == 'B' | ('M' << 8) // 0x4D42 (CPU がリトルエンディアンの場合)
bfType == ('B' << 8) | 'M' // 0x424D (CPU がビッグエンディアンの場合)
0やデタラメな値が入っていることがあるので, 読むときにはこれをアテにしてはいけない. ファイル全体のサイズを知らなくても BMP ファイルは読める. そもそも,BMP ファイルを丸ごとメモリに読み込むというのはあまり良い方法ではない. BITMAPINFO とビットマップデータの間に不要な隙間がある場合もあるし, BITMAPFILEHEADER は読込み後は不要である (場合によっては BITMAPINFO も).
typedef struct tagBITMAPCOREHEADER {
DWORD bcSize; /* used to get to color table */
WORD bcWidth;
WORD bcHeight;
WORD bcPlanes;
WORD bcBitCount;
} BITMAPCOREHEADER;
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;
0やデタラメな値が入っていることがあるので, これをアテにしてはいけない.後述するように BI_RGB や BI_BITFIELDS の場合,biSizeImage の正しい値は biWidth,biHeight,biBitCount から計算することができるので, この値を使う必要はない.
どう使えばいいのかわからない.Windows SDK v6.1 のマニュアルを "biClrImportant" で検索してみても,biClrImportant を0または最大色数に設定している例しか見つからない. 作成するときは常に0を設定しておけばたぶん OK.
| 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画素を表す. |
| 定数名 | 値 | 説明 |
|---|---|---|
| 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「ファイル」で使用するための値ではない. これらの値は, SetDIBitsToDevice() や StretchDIBits() を用いて,JPEGやPNG画像をサポートするプリンタに JPEG/PNG データをそのまま出力 (丸投げ,JPEG/PNG pass-through) する場合に使用するためのもの.(Win32 API (GDI) がこれらの API に対して JPEG/PNG の圧縮・伸張をサポートするわけではない.) 参考 |
| BI_PNG | 5 |
この構造体は 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;
| 定数名 | 値 | 説明 |
|---|---|---|
| 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 は使用するプロファイルを含むメモリバッファを指す. |
補足:マニュアルにはあまり説明がないが,ここでいう“論理色空間”は CIE 1931 XYZ 色空間を指すと考えられる.この空間の中で赤,緑,青に対応する3つの endpoint (RGB の色域となる三角形の頂点) を指定することで,さまざまな RGB 色空間を定義することができる.また,マニュアルには "x, y, z" と小文字で書かれているが,"X, Y, Z" (大文字) が正しいはず (小文字の xyz 座標には輝度情報がほとんど含まれない).
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;
| 値 | 意図 | 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) | ビジネス・グラフィックスやディザなしの色が必要な場合向け.色の相対的彩度を維持する. |
■参考
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;
BYTE bitmapData[<走査線の本数>][<走査線のバイト数>];
ビットマップ情報ヘッダが BITMAPV5HEADER で, かつ bv5CSType が PROFILE_LINKED または PROFILE_EMBEDDED の場合にのみ存在する.
4 BPP 以下の画像では,1バイトに複数の画素値が入っている. 走査線は4バイト境界にアラインされているので, 4バイト単位 (場合によっては2バイト単位) で処理することにより高速化を図ることも可能だが, 次の点に注意する必要がある.
1バイトの中では,走査線内の画素は上位ビットから下位ビットに向かって格納される. つまりバイト内での画素の格納順序はビッグエンディアンである. 1 BPP の場合,バイト内の各ビットは次のような配置で画像に表示される. 図中の番号はバイト内のビット位置を表す (LSB=0,MSB=7).
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
ビットマップデータを複数バイト単位で処理する場合,CPU のエンディアンが問題となる. ビットマップデータの4バイトを1つの4バイト整数として扱う場合, ビッグエンディアン CPU では整数内の各ビットは次のような配置で画像に表示される (LSB=0,MSB=31).
|
|
|
|
一方,リトルエンディアン CPU では次のようになる.
|
|
|
|
つまりリトルエンディアン CPU では,
2バイトまたは4バイト整数内のビット (画素) 順と,
画像上の画素順が一致していない.
このためビットマップデータをバイト単位で処理していたのを,
高速化のためそのまま2バイトまたは4バイト単位で処理するように変更しようとしても,
画素の順序が狂ってしまってうまくいかない場合がある.
(私も最近やっちゃいました.(^^;))
今までに何度か,他人様の書いた BMP ファイルを読み書きするコードを見る機会があったが, あまりにひどいものがいくつかあったので,ここに参考コーディング例を載せることにした. 「ひどい」の内容は次のとおり.
とりあえず,バグの原因になりやすい,
ビットマップデータサイズの計算方法を中心に書くことにする.
(そのうち気が向いたら他の部分も書きます.)
画像の幅を width,高さを height とする. まず,1本の走査線のサイズ (DWORD 数) lineSizeDW を次のようにして計算する. (8 BPP の場合はどちらの方法でもよい. また 2 BPP は BMP ファイルの仕様には含まれていないが, もし存在しても 1 BPP や 4 BPP の場合と同様に計算できる.)
DWORD pixelsPerDW = 8 * sizeof(DWORD) / BPP; // DWORD 内の画素数 // 走査線のサイズ (DWORD 数) を計算する. // これは width / pixelsPerDW の小数部を切り上げた値である. DWORD lineSizeDW = ICEIL(width, pixelsPerDW);
DWORD bytesPerPixel = BPP / 8; // 1画素当たりのバイト数 // まず,走査線の正味のバイト数を計算する. DWORD lineSizeDW = bytesPerPixel * width; // lineSizeDW を実際のサイズ (DWORD 数) にするため, // sizeof(DWORD) で割る (小数部切り上げ). lineSizeDW = ICEIL(lineSizeDW, sizeof(DWORD));
次に,走査線のサイズ (バイト数) lineSize とビットマップデータの全バイト数 imageSize を求める.
DWORD lineSize = lineSizeDW * sizeof(DWORD); DWORD imageSize = lineSize * height;
上で計算した 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 の全メンバのエンディアンを反転させること.
未整理,敬称略.
|
Copyright © 2006-2017 noocyte E-mail: relipmoced (a) yahoo.co.jp (" (a) " を半角のアットマークに書き替えてください.) リンクはご自由に. 「noocyte のプログラミング研究室」トップページに戻る. |