DDBの作成
デバイス依存ビットマップ
ビットマップは、過去何度か Windows のバージョンにあわせて仕様を変えています
そのため、API を直接操作する場合はその仕様をある程度理解する必要があります
なぜ、何度も仕様を拡張しているのでしょう
それは、元々ビットマップは OS/2 で誕生したフォーマットだからです
DDB (device-dependent bitmap)はデバイス依存ビットマップと呼ばれ
前回作成した GDI オブジェクトの一つである HBITMAP 型は DDB です
(ただし、読みこんだビットマップは DDB ではない)
今回は、リソースからではなくゼロからビットマップを作成します
そこで、最初は簡単なモノクロビットマップから生成しようと思います
ビットマップの作成には、まず CreateBitmap() 関数を使用します
HBITMAP CreateBitmap(
int nWidth , int nHeight ,
UINT cPlanes,
UINT cBitsPerPel,
CONST VOID *lpvBits
);
nWidth はビットマップの幅、、nHeight はビットマップの高さを指定します
cPlanes はデバイスが使用するカレープレーン
cBitsPerPel は1ピクセルが色を識別するために必要なビット数を指定します
lpvBits には、ピクセルの色データが格納された配列へのポインタを指定します
成功すればビットマップのハンドル、失敗すれば NULL を返します
カラープレーンとビット数は何を指定すればよいのでしょうか?
論理的には、どのような仕様のビットマップでも作成してよいことになります
しかし、カラープレーン 3、1 ピクセルに対して 10 ビット使うビットマップを作成しても
SelectObject() 関数はモノクロかデバイスに互換性がないと動作しません
そのため、奇妙なビットマップをつくっても意味はないのです
主に CreateBitmap() 関数はモノクロビットマップを作成する時のみに使います
cPlanes と cBitsPerPel には 1 を指定すればよいでしょう
ビット列は、前景色が 0、背景色が 1 となりますが
NULL を渡せばピクセルビットは初期化されません
モノクロでは、1 ピクセルは 1 ビットで表現されています
この性質を知れば、BYTE 型を用いてピクセルを表現できます
BYTE 型は符合無し8ビットの変数で、これを表現するのに適しているのです
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc , hBuffer;
PAINTSTRUCT ps;
static HBITMAP hBitmap;
static BYTE px[] = { 0xFF , 0 , 0xFF , 0 , 0xFF , 0 ,
0 , 0xFF , 0 , 0xFF , 0 , 0xFF ,
0xFF , 0 , 0xFF , 0 , 0xFF , 0 ,
0 , 0xFF , 0 , 0xFF , 0 , 0xFF };
switch (msg) {
case WM_DESTROY:
DeleteObject(hBitmap);
PostQuitMessage(0);
return 0;
case WM_CREATE:
hBitmap = CreateBitmap(48 , 4 , 1 , 1 , px);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd , &ps);
hBuffer = CreateCompatibleDC(hdc);
SelectObject(hBuffer , hBitmap);
StretchBlt(
hdc , 0 , 0 , 400 , 120 , hBuffer ,
0 , 0 , 48 , 4 , SRCCOPY
);
DeleteDC(hBuffer);
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;
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;
}
横に8ピクセル白いライン(0xFF)が描かれ、その後8ピクセルは黒いライン(0)です
これをジグザグに描画した小さなビットマップを StretchBlt() で拡大しています
また、これとは別の方法で BITMAP 構造体 を使用する方法もある
BITMAP 構造体を使う方法も、先ほどのものと大差はない
typedef struct tagBITMAP {
LONG bmType;
LONG bmWidth;
LONG bmHeight;
LONG bmWidthBytes;
WORD bmPlanes;
WORD bmBitsPixel;
LPVOID bmBits;
} BITMAP;
bmType メンバは、0 でなければいけません
bmWidth はビットマップの幅、bmHeight は高さを指定します
bmWidthBytes は各行のバイト数ですが、これは直接設定する必要はありません
基本的に、このメンバは Windows が計算してくれます
bmPlanes は、カラープレーン、bmBitsPixel は1ピクセルのカラービット数です
bmBits は、ピクセルビットの配列へのポインタを格納します
ここで指定した BITMAP 構造体を CreateBitmapIndirect() 関数に渡します
HBITMAP CreateBitmapIndirect(CONST BITMAP *lpbm);
lpbm は、BITMAP 構造体へのポインタを指定します
成功すればビットマップハンドル、失敗すれば NULL が返ります
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
HDC hdc , hBuffer;
PAINTSTRUCT ps;
static BITMAP bmBitmap;
static HBITMAP hBitmap;
static BYTE px[] = { 0xFF , 0 , 0xFF , 0 , 0xFF , 0 ,
0 , 0xFF , 0 , 0xFF , 0 , 0xFF ,
0xFF , 0 , 0xFF , 0 , 0xFF , 0 ,
0 , 0xFF , 0 , 0xFF , 0 , 0xFF};
switch (msg) {
case WM_DESTROY:
DeleteObject(hBitmap);
PostQuitMessage(0);
return 0;
case WM_CREATE:
bmBitmap.bmType = 0;
bmBitmap.bmWidth = 48;
bmBitmap.bmHeight = 4;
bmBitmap.bmPlanes = bmBitmap.bmBitsPixel = 1;
bmBitmap.bmBits = px;
hBitmap = CreateBitmapIndirect(&bmBitmap);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd , &ps);
hBuffer = CreateCompatibleDC(hdc);
SelectObject(hBuffer , hBitmap);
StretchBlt(
hdc , 0 , 0 , 400 , 120 , hBuffer ,
0 , 0 , 48 , 4 , SRCCOPY
);
DeleteDC(hBuffer);
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;
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;
}
結果は、先ほどのプログラムとまったく同じです
違うのは HBITMAP 変数、すなわち DDB の作成の過程だけです
CreateBitmap()
HBITMAP CreateBitmap(
int nWidth , int nHeight ,
UINT cPlanes,
UINT cBitsPerPel,
CONST VOID *lpvBits
);
指定された幅、高さ、色形式を持つビットマップを作成します
通常は、1ピクセル1ビットのモノクロビットマップの作成に使用してください
nWidth - ビットマップの幅をピクセル単位で指定します
nHeight - ビットマップの高さをピクセル単位で指定します
cPlanes - デバイスが使用するカラープレーンの数を指定します
cBitsPerPel - 1 ピクセルの色を識別するのに必要なビット数を指定します
lpvBits - ピクセルの色データが入った配列へのポインタを指定します
戻り値 - ビットマップのハンドル。失敗すれば NULL
CreateBitmapIndirect()
HBITMAP CreateBitmapIndirect(CONST BITMAP *lpbm);
指定された幅、高さ、色形式を持つビットマップを作成します
通常は、1ピクセル1ビットのモノクロビットマップの作成に使用してください
lpbm - ビットマップの情報が格納されている BITMAP 構造体へのポインタを指定します
戻り値 - ビットマップのハンドル。失敗すれば NULL