Windows DIB
Windows 版の情報ヘッダ
さて、これまで説明してきた DIB の仕様は OS/2 のものです
現在の Windows アプリケーションでこのタイプの DIB はごく稀で
Windows の場合は Windows3.0 で同時に導入された拡張された仕様が一般的です
通常の Windows アプリケーションが出力するビットマップファイルの多くは
この Windows3.0 で導入された DIB であり
OS/2 仕様の DIB はフォトショップなどの高レベルなCG制作環境などで
明示的に OS/2 仕様の DIB を出力することを指定しなければ、まず見ることはありません
Windows3.0 では従来の OS/2 DIB を拡張した新しいヘッダを定義しました
画像の描画という点では、OS/2 仕様の DIB でも十分なのですが
Windows ではプリンタなどの様々なデバイスに対応するための情報を追加したのです
Win32 API では BITMAPINFOHEADER 構造体として定義しています
typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
基本的には従来の BITMAPCOREHEADER を拡張したものですが
biWidth と biHeight が LONG の 32 ビット に再定義されていることに注意してください
これ以外では、biBitCount までのメンバの意味は従来のものと同じです
biSize は構造体のサイズ 40 (0x28) が格納されます
この構造体のサイズで、OS/2 の DIB か Windows DIB かを確認できます
また、biBitCount は、さらに 16 と 32 を指定することが可能になっています
さほど重要ではないので省略しますが、このことは覚えておくとよいでしょう
biCompression は圧縮コードを表す
未圧縮の BI_RGB、4 ビット DIB の圧縮 BI_RLE4、8 ビットの圧縮 BI_RLE8 のいずれかで
2色と 24 ビット DIB の場合は常に BI_RGB しか選択できません
この場は圧縮技術について説明するものではないので、詳しくは省略します
RLE 圧縮形式は、単純に連続したバイトをまとめる簡単な圧縮技術で
多くのアプリケーションは、ビットマップの RLE 圧縮をサポートしていません
biSizeImage はイメージのバイト数を指定します
biXPelsPerMeter は水平解像度、biYPelsPerMeter は垂直解像度を表します
これらは 1 メートルあたりのピクセル数を表し、実世界での画像サイズを提供しています
しかし、情報を提供しない場合は 0 を指定します(通常は 0 です)
biClrUsed はカラーテーブルの数を任意に指定できる重要なメンバです
インデックスカラーの DIB では、カラーテーブルは常に 16 または、256 個の固定でしたが
Windows 版では biBitCount 未満の値を biClrUsed に指定することによって
それ以下のカラーテーブルを保有することができます
biClrUsed に 0 を指定すれば、biBitCount からカラーテーブルのエントリ数を決定します
biClrImportant は重要な色の数を表します
この値は、複数の 256 色の DIB を同時に表示するような場合に
最低でもカラーテーブルの先頭から biClrImportant までの色は正確に表示し
イメージの品質を損なわない様に表示してほしいという意思を表します
このような理由から、カラーテーブルでは重要な色ほど先頭にするという習慣がありますが
実際に、重要な色が守られて表示されるという保証があるわけでもありません
そのため、通常は全ての色が重要であることを表す 0 または biClrUsed と同じ値を指定します
#include <windows.h>
BITMAPFILEHEADER bmpFileHeader;
BITMAPINFOHEADER bmpInfoHeader;
LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
static TCHAR chStr[1024];
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_CREATE:
wsprintf(chStr ,
TEXT("FILEHEADER\n")
TEXT("bfType = %X\nbfSize = %d\nbfReserved1 = %d\n")
TEXT("bfReserved2 = %d\nbfOffBits = %d\n\n")
TEXT("INFOHEADER\n")
TEXT("biSize = %d\nbiWidth = %d\nbiHeight = %d\n")
TEXT("biPlanes = %d\nbiBitCount = %d\n")
TEXT("biCompression = %d\nbiSizeImage = %d\n")
TEXT("biXPelsPerMeter = %d\nbiYPelsPerMeter = %d\n")
TEXT("biClrUsed = %d\nbiClrImportant = %d") ,
bmpFileHeader.bfType , bmpFileHeader.bfSize ,
bmpFileHeader.bfReserved1 , bmpFileHeader.bfReserved2 ,
bmpFileHeader.bfOffBits ,
bmpInfoHeader.biSize , bmpInfoHeader.biWidth ,
bmpInfoHeader.biHeight , bmpInfoHeader.biPlanes ,
bmpInfoHeader.biBitCount , bmpInfoHeader.biCompression ,
bmpInfoHeader.biSizeImage ,
bmpInfoHeader.biXPelsPerMeter ,
bmpInfoHeader.biYPelsPerMeter ,
bmpInfoHeader.biClrUsed , bmpInfoHeader.biClrImportant
);
return 0;
case WM_PAINT:
hdc = BeginPaint(hWnd , &ps);
GetClientRect(hWnd , &rect);
DrawText(hdc , chStr , -1 , &rect , DT_LEFT);
EndPaint(hWnd , &ps);
return 0;
}
return DefWindowProc(hWnd , msg , wp , lp);
}
int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
PSTR lpCmdLine , int nCmdShow) {
HWND hWnd;
MSG msg;
WNDCLASS winc;
HANDLE hFile;
DWORD dwBytes;
hFile = CreateFile(lpCmdLine , GENERIC_READ , 0 , NULL ,
OPEN_EXISTING , FILE_ATTRIBUTE_NORMAL , NULL);
if (hFile == INVALID_HANDLE_VALUE) return 1;
ReadFile(hFile , &bmpFileHeader , sizeof (BITMAPFILEHEADER) , &dwBytes , NULL);
if (bmpFileHeader.bfType != 0x4D42) {
MessageBox(NULL , TEXT("This is not a bitmap file") , NULL , MB_OK);
return 1;
}
ReadFile(hFile , &bmpInfoHeader , sizeof (BITMAPINFOHEADER) , &dwBytes , NULL);
CloseHandle(hFile);
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = WndProc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = hInstance;
winc.hIcon = LoadIcon(NULL , IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL , IDC_ARROW);
winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
winc.lpszMenuName = NULL;
winc.lpszClassName = TEXT("KITTY");
if (!RegisterClass(&winc)) return 1;
hWnd = CreateWindow(
TEXT("KITTY") , TEXT("Kitty on your lap") ,
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
CW_USEDEFAULT , CW_USEDEFAULT ,
CW_USEDEFAULT , CW_USEDEFAULT ,
NULL , NULL , hInstance , NULL
);
if (hWnd == NULL) return 1;
while (GetMessage(&msg , NULL , 0 , 0 )) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
このプログラムは、コマンドラインから指定した Windows DIB のヘッダ情報を
上の画像のように、ウィンドウに列挙するというものです
OS/2 DIB の情報ヘッダのプログラムと異なり
このプログラムは Windows の *.bmp ファイルのほとんどに対応するでしょう
さて、これで Windows DIB の情報ヘッダを取得することができるようになりました
しかし、さらに Windows DIB ではカラーテーブルも拡張されています
Windows 版 DIB では、RGBTRIPLE 構造体ではなく RGBQUAD 構造体を使います
インデックスカラー形式の DIB をバイナリレベルで扱う場合は覚えておかなければなりません
typedef struct tagRGBQUAD { // rgbq
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
RGBQUAD は OS/2 仕様に比べ、rgbReserved メンバが追加されています
この新しいメンバは、常に 0 を格納していなければなりません
これは、カラーテーブルが 32 ビットのピクセルビットに対応するためです
BITMAPINFOHEADER の biBitCount が 32 を指定することが可能になったと説明しましたが
32ビットのピクセルビットは、1ピクセルが RGBQUAD で表現されていると思えば
カラーテーブルが拡張されて 32 ビットになったことも肯けるはずです
情報ヘッダとカラーテーブルの配列をパック化した BITMAPCOREINFO 構造体に対し
RGBQUAD 構造体に対応した BITMAPINFO 構造体も定義されています
typedef struct tagBITMAPINFO { // bmi
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO;
これは、BITMAPCOREINFO 構造体の RGBTRIPLE 型の bmciColors メンバが
Windows 用に RGBQUAD 型の bmiColors メンバに変更されたものです
ただし、カラーテーブルは biClrUsed で任意の数に指定できるようになっているので
OS/2 のように単純に操作できるわけではないことに注意してください
さて、これで終わればビットマップの仕様はそれほど難しいものではなかったでしょう
ところが、Windows 95 と NT 4.0 でさらに独自のフィールドが設けられています
それが、BITMAPV4HEADER 構造体です
typedef struct {
DWORD bV4Size;
LONG bV4Width;
LONG bV4Height;
WORD bV4Planes;
WORD bV4BitCount;
DWORD bV4V4Compression;
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;
さらに、Windows 98 と Windows 2000 でもさらなる定義が行われました
比較的新しい OS を対象にしたプログラムは、これを使うことができます
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;
#define BI_JPEG 4L
これらの新しい定義では、DIB の圧縮コードに JPEG もサポートするなどの拡張があり
とくに、従来の DIB に比べて豊富な色情報を含ませることができるようになっています
これによって、デバイスや加法混色、減法混色による色のギャップを最小にするためです
RGB 方式(加法混色)では、色の強さを数値化しているものであって
同じ RGB(0xFF , 0 , 0) という色でも、赤い電子銃の出力を最大にするという意味です
全てのデバイスでまったく同じ赤色が再現されることを保証するものではありません
この情報ヘッダでは、そういった問題点をカバーするために
科学的に色を定量化した CIE XYZ 値という情報を提供し
BITMAPV5HEADER では、さらにデバイス依存カラーと非依存カラーの関係を示します
ICC Profile Format Specification というプロファイルなどを保有(またはリンク)することができるのです
この場は Win32 API を解説を目的としていて、グラフィックス理論を目的としていません
これらのヘッダが持つバイナリレベルの情報の意味は、より専門的になってしまいます
とりあえず、このような情報ヘッダも定義されているということを知ってくれれば良いでしょう
もし、イメージの色に対してデバイスに依存しない科学的なアプローチを試みたいのであれば
グラフィックスに関連した専門書を読んで学習すると良いでしょう
トップダウン DIB
まったくの余談かもしれませんが、トップダウン DIB というものも存在します
Windows 版の DIB の情報ヘッダは、95、98 と Windows がバージョンアップするごとに
基本仕様はそのままで、一部再定義されたりしています
現在の仕様では DIB をより直感的に扱えるように
OS/2 のボトムアップ形式以外に、トップダウンの DIB
すなわち、イメージの左上が原点として扱える DIB を作成することができます
トップダウンの DIB は BITMAPINFOHEADER の biHeight が負数となります
つまり、 biHeight が負数ならば、それはトップダウンの DIB と考えられます
あなたが作成するアプリケーションが、このタイプの DIB の描画に対応することは良いことですが
あなたが作成したアプリケーションが、このタイプの DIB を出力することは推奨しません
現在でも、多くの DIB は OS/2 のボトムアップ DIB であり
古いアプリケーションなどは、トップダウン DIB をサポートしていないでしょう