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 をサポートしていないでしょう



前のページへ戻る次のページへ