DIB セクション


DIB と DDB の相互参照

これまでの説明で、DIB の仕様と DDB との関係はある程度理解していただけたと思う
しかし、その関係は複雑で、非常に面倒な処理が多く存在しました
ピクセルビットを読み込むために、面倒な計算とメモリ確保が必要でしたし
DIB を DDB に変換したり、それを元に戻したりという作業も時には必要です

実は、CreateDIBSection() という便利な関数が存在します
これは、Win32 で追加された API で、DIB を DDB の様に扱えます
結果的には DIB と DDB に相互関係を築くことができるといえます

この関数は、BITMAPINFO 構造体の情報を参照して
ピクセルビットを格納するメモリ領域を確保し
その領域のピクセルデータと相互関係を持ったビットマップハンドルを返します
HBITMAP CreateDIBSection(
	HDC hdc ,
	CONST BITMAPINFO *pbmi ,
	UINT iUsage ,
	VOID *ppvBits ,
	HANDLE hSection ,
	DWORD dwOffset
);
hdc にはデバイスコンテキストのハンドルを指定します
この引数は iUsage に DIB_PAL_COLOS を指定した時のみ使用されます
iUsage が DIB_RGB_COLORS の場合は無視されるので NULL でもかまいません

pbmi は DIB の情報を表す BITMAPINFO 構造体へのポインタを、
iUsage はカラー情報フラグ (SetDIBitsToDevice() 参照) を指定します

ppvBits はピクセルビットへのポインタのポインタを指定します
これは、関数がこれにメモリ領域を割り当てるのに使用されます
割り当てるメモリサイズは pbmi の情報を元に
DIB ピクセルビットを格納できる十分な領域を確保されます

hSection には、ファイルマッピングオブジェクトを指定することができます
ここには、CreateFileMapping() という関数が返したハンドルを指定します
ファイルマッピングとは、メモリポインタがファイルにアクセスすることによって
ファイルがメモリに配置されているかのように振舞う機能です

少ないメモリ領域で、巨大なファイルを扱う時によく用いられる方法ですが
この方法はまだ解説していないので、この場は NULL を指定してください
一般的に、この関数でこの機能を用いることはほとんどないでしょう

最後の dwOffset は、hSection を指定した時にピクセルビットのオフセットを指定します
ただし、この値は32の整数倍でなければなりません
hSection が NULL の場合、このパラメータは無視されます

関数が成功するとビットマップのハンドル、失敗すると NULL が返ります

さて、これが CreateDIBSection() 関数の構文ですが
この関数はこれまでの DIB と DDB の常識から考えると奇妙な点があります
結局この関数は、DDB を作っているのか DIB を作っているのか曖昧ですね

正確には、この関数が返すビットマップは DDB ではないのです
このビットマップのハンドルは、暗黙的にシステム上の DIB ピクセルビットを参照します
もしこの関数が DDB を作成するならば、第一引数の hdc は必須になるはずです
しかし、この関数はデバイスに依存したデータに変換することはないため
hdc はデバイスコンテキストのパレットを参照するための引数にすぎないのです

ところが、不思議なことに CreateDIBSection() が返したビットマップは
BitBlt() 等の DDB 専用の関数で問題なく使用することができます
これが、この関数の威力です

実は、この関数が返したビットマップハンドルは特殊なもので
このハンドルは BitBlt() などで使用される時に DDB に変換されます
BitBlt() や StretchBlt() 関数は、CreateDIBSection() が返したハンドルを受けると
DIB ピクセルビットを出力先のデバイスに依存した DDB に暗黙的に変換するのです

さらにこのビットマップハンドルは、メモリデバイスコンテキストに設定して
GDI 関数を用いて様々な描画処理を施すことができるのです
すばらしいことに、その結果は DIB のピクセルに反映されます

CreateDIBSection() 関数が割り当てたピクセルビットの領域はシステムが管理します
関数が返したビットマップハンドルが破棄された時、同時にこの領域も破棄されます
直接 free() で破棄してはいけないので、注意してください
#include <windows.h>

TCHAR strFileName[1024];
BITMAPFILEHEADER bmpFileHeader;
BITMAPINFO * bmpInfo;
BYTE * bPixelBits;
HBITMAP hBitmap;

LRESULT CALLBACK WndProc(HWND hWnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	HANDLE hFile;
	PAINTSTRUCT ps;
	DWORD dwBytes;
	static BITMAP bitmap;
	static HDC hBuffer;

	switch (msg) {
	case WM_DESTROY:
		free(bmpInfo);
		DeleteDC(hBuffer);
		DeleteObject(hBitmap);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		hdc = GetDC(hWnd);

		hBuffer = CreateCompatibleDC(hdc);
		SelectObject(hBuffer , hBitmap);
		GetObject(hBitmap , sizeof (BITMAP) , &bitmap);

		BitBlt(	hBuffer , 0 , 0 , bitmap.bmWidth , bitmap.bmHeight ,
			hBuffer , 0 , 0 , NOTSRCCOPY
		);
		InvalidateRect(hWnd , NULL , TRUE);

		ReleaseDC(hWnd , hdc);

		hFile = CreateFile(strFileName , GENERIC_WRITE , 0 , NULL ,
			CREATE_NEW , FILE_ATTRIBUTE_NORMAL , NULL);
		if (hFile == INVALID_HANDLE_VALUE) return 0;

		WriteFile(	hFile , &bmpFileHeader ,
			sizeof (BITMAPFILEHEADER) , &dwBytes , NULL);
		WriteFile(	hFile , bmpInfo ,
			bmpFileHeader.bfOffBits - sizeof (BITMAPFILEHEADER) ,
			&dwBytes , NULL);
		WriteFile( hFile , bPixelBits ,
			bmpFileHeader.bfSize - bmpFileHeader.bfOffBits ,
			&dwBytes , NULL);
		CloseHandle(hFile);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hWnd , &ps);

		BitBlt(	hdc , 0 , 0 , bitmap.bmWidth , bitmap.bmHeight ,
			hBuffer , 0 , 0 , SRCCOPY
		);

		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;
	}

	bmpInfo = (BITMAPINFO *) malloc (bmpFileHeader.bfOffBits - dwBytes);
	ReadFile(hFile , bmpInfo , bmpFileHeader.bfOffBits - dwBytes , &dwBytes , NULL);
	hBitmap = CreateDIBSection(
		NULL , bmpInfo , DIB_RGB_COLORS , &bPixelBits , NULL , 0
	);
	ReadFile(hFile ,bPixelBits ,
		bmpFileHeader.bfSize - bmpFileHeader.bfOffBits , &dwBytes , NULL);
	CloseHandle(hFile);

	*(lpCmdLine + lstrlen(lpCmdLine) - 4) = 0;
	wsprintf(strFileName , TEXT("%s_not.bmp") , lpCmdLine);

	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;
}
このプログラムは、前回紹介したピクセルを反転してディスクに保存するプログラムです
前回は CreateDIBitmap() で DIB を DDB に変換して
GetDIBits() で再びピクセルを DIB に変換するという方法で行いました

しかし、今回はそのようなことはまったくしていません
WinMain() 関数で CreateDIBSection() を用いてビットマップハンドルを作成しているだけです
ピクセルビットを格納するメモリ領域は、この関数が作成してくれます
CreateDIBSection() のすぐ後に、確保した領域にピクセルビットを読み込んでいます

後は、このビットマップハンドルをメモリデバイスコンテキストに割り当て
さらに何らかの描画処理などをすれば、それはそのまま DIB に反映されます

そういえば、以前 GetObject() 関数を紹介した時に見たかもしれませんが
GetObject() 関数で HBITMAP を第一引数に指定した時
BITMAP 構造体以外に、もう一つ特殊な構造体で情報を取得することもできました
それは DIBSECTION 構造体です
typedef struct tagDIBSECTION { 
    BITMAP              dsBm; 
    BITMAPINFOHEADER    dsBmih; 
    DWORD               dsBitfields[3]; 
    HANDLE              dshSection; 
    DWORD               dsOffset; 
} DIBSECTION;
dsBm には BITMAP 構造体、dsBmih には DIB の情報ヘッダ、
dsBitfields にカラーマスク、dshSection 派ファイルマッピングオブジェクトのハンドル
そして、dsOffset はビットマップピクセルビットのオフセットを表します

この構造体を見てわかるように、通常のビットマップハンドル作成関数で作った
ビットマップハンドルでは、この情報を取得することはできません
これは、CreateDIBSection() が返したビットマップハンドル専用の情報です
CreateDIBSection() で作成したビットマップハンドルであれば
GetObject() 関数を使用して DIBSECTION 構造体に情報を取得することができます


CreateDIBSection()

HBITMAP CreateDIBSection(
	HDC hdc ,
	CONST BITMAPINFO *pbmi ,
	UINT iUsage ,
	VOID *ppvBits ,
	HANDLE hSection ,
	DWORD dwOffset
);
GDI とピクセルビットデータの両方から操作できる DIB を作成します
この DIB は GDI ファンクションで描くこともできますし
直接ピクセルデータにアクセスして描くことも可能です

hdc - デバイスコンテキストのハンドルを指定します
pbmi - DIB の情報を示す BITMAPINFO へのポインタを指定します
iUsage - カラー情報フラグを指定します
hSection - ファイルマッピングオブジェクトを指定します
dwOffset - セクションの先頭からピクセルビットのオフセットを指定します

戻り値 - ビットマップのハンドル。失敗すると NULL

定数解説
DIB_PAL_COLORS カラーテーブルに論理パレットに対する
16 ビットインデックスが格納されている
DIB_RGB_COLORS カラーテーブルに RGB 値が格納されている



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