リージョンの生成


領域の指定と描画

以前、無効リージョンの部分でリージョンという言葉を使いました
リージョンとは、画面内の領域を表すオブジェクトの一つで、専用のハンドルを持ちます

グラフィカルプログラムにおいて、リージョンの知識はなくてはならないものです
なぜならば、リージョンは矩形や多角形、円などの複数の図形を合わせた
複雑な領域を表す一つのハンドルだからです

私たちは今まで、描画処理をそれぞれ固有の形をあらわすファンクションで行いました
しかし、Rectangle や Ellipse 等の閉じた図形はリージョンとしてあらわすこともできます
それも、描画したい形をリージョンとして登録するだけなので
描画したい形によって、ファンクションを使い分ける必要がありません

リージョンのハンドルは HRGN 型です
リージョンを生成するには、様々な専用ファンクションを用います

まず、簡単な矩形のリージョンを作ってみましょう
矩形のリージョンを作るには CreateRectRgn() ファンクションを用います
HRGN CreateRectRgn(
	int nLeftRect , int nTopRect ,
	int nRightRect , int nBottomRect
);
nLeftRect には始点X座標、nTopRect には始点Y座標
nRightRect は終点X座標、nButtomRect は終点Y座標を指定します
指定した始点と終点のサイズで表された矩形のリージョンを返します

同じような関数で CreateRectRgnIndirect() もあります
これは、RECT 構造体を直接引数に渡すファンクションです
それ以外では、CreateRectRgn() 関数と同じです

HRGN CreateRectRgnIndirect(CONST RECT *lprc);

次に、これらの関数が返したリージョンを描画しましょう
FillRgn() ファンクションを用いれば、指定ブラシでリージョンを塗りつぶします

BOOL FillRgn(HDC hdc , HRGN hrgn , HBRUSH hbr);

hdc には、デバイスコンテキストへのハンドル
hrgn は、描画するリージョンのハンドルを指定します
hbr は、塗りつぶすブラシのハンドルを指定します

戻り値は、関数が成功すると 0 以外、失敗すると0が返ります
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HRGN hrgn;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);

		hrgn = CreateRectRgn(10 , 10 , 100 , 100);
		FillRgn(hdc , hrgn , GetStockObject(BLACK_BRUSH));
		DeleteObject(hrgn);

		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)) DispatchMessage(&msg);
	return msg.wParam;
}


これだけでは、あまりありがたみが感じられないかもしれませんね
しかし、状況によって描画する形を変化させなければならない場合
描画ファンクションはFillRgn()等を使い、形の設定は別の処理として行えます
ユーザーに形を設定させる画像編集ソフトなどの分野で、性能を発揮するでしょう

描画には、FillRgn() 以外にもファンクションが用意されています
リージョンの境界(枠)を描画したい場合は FrameRgn() 関数が有効です
BOOL FrameRgn(
	HDC hdc ,
	HRGN hrgn ,
	HBRUSH hbr ,
	int nWidth , int nHeight
);
hdc には、デバイスコンテキストのハンドルを
hrgn には、リージョンのハンドルを
hbr には、境界を描画するためのブラシのハンドルを指定します

nWidth は垂直方向の線の幅、nHeight には水平方向の選の幅を指定します
関数が成功すると 0 以外、失敗すると 0 が返ります

また、FillRgn() でブラシの生成などが面倒であれば PaintRgn() 関数があります
FillRgn() と動作は同じですが、デバイスコンテキストに設定されているブラシを使います

BOOL PaintRgn(HDC hdc , HRGN hrgn);

この関数は FillRgn() の第三引数をなくしただけです
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HRGN hrgn;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);
		hrgn = CreateRectRgn(10 , 10 , 100 , 100);
		SelectObject(hdc , CreateHatchBrush(HS_DIAGCROSS , RGB(0xFF , 0 , 0)));

		PaintRgn(hdc , hrgn);
		FrameRgn(hdc , hrgn , GetStockObject(GRAY_BRUSH) , 2 , 5);

		DeleteObject(hrgn);
		DeleteObject(SelectObject(hdc , GetStockObject(BLACK_BRUSH)));

		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)) DispatchMessage(&msg);
	return msg.wParam;
}


どうでしょうか、なかなかリージョンの強力な一面が出てきましたね
周囲の枠は FrameRgn() を、中の網は PaintRgn() を使っています


様々なリージョン

リージョンは、なにも矩形だけではありません
先ほど説明したように、円や多角形など様々なリージョンを作れます
ここでは、様々な形のリージョンを描画してみましょう

楕円のリージョンを生成するには CreateEllipticRgn() ファンクションを用います
HRGN CreateEllipticRgn(
	int nLeftRect , int nTopRect,
	int nRightRect , int nBottomRect
);
nLeftRect は左上隅のX座標、nTopRect は左上隅のY座標
nRigthRect は右下隅のX座標、nButtomRect は右下隅のY座標を指定します
指定した長方形に外接する楕円のリージョンを返します

同様に、CreateEllipticRgnIndirect()を用いれば
RECT 構造体で楕円を指定することもできます

HRGN CreateEllipticRgnIndirect(CONST RECT *lprc;);

lprc には RECT 構造体へのポインタを指定します
指定した構造体が示す長方形に外接する楕円のリージョンが返ります
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HRGN hrgn;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);
		SelectObject(hdc , GetStockObject(GRAY_BRUSH));

		hrgn = CreateEllipticRgn(10 , 10 , 200 , 200);
		PaintRgn(hdc , hrgn);

		DeleteObject(hrgn);
		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)) DispatchMessage(&msg);
	return msg.wParam;
}


楕円のリージョンを生成できたことが確認できますね

同様の方法で、角の丸い矩形をリージョンにすることもできます
角の丸いリージョンは CreateRoundRect() ファンクションを用います
HRGN CreateRoundRectRgn(
	int nLeftRect , int nTopRect ,
	int nRightRect , int nBottomRect ,
	int nWidthEllipse , int nHeightEllipse 
);
nLeftRect は左上隅のX座標、nTopRect は左上隅のY座標
nRigthRect は右下隅のX座標、nButtomRect は右下隅のY座標を指定します
nWidthEllipse は角の丸みの幅、nHeightEllipse は丸みの高さを指定します
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HRGN hrgn;

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);
		SelectObject(hdc , GetStockObject(GRAY_BRUSH));

		hrgn = CreateRoundRectRgn(10 , 10 , 200 , 100 , 50 , 20);
		PaintRgn(hdc , hrgn);

		DeleteObject(hrgn);
		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)) DispatchMessage(&msg);
	return msg.wParam;
}


これも、とくに問題ないと思います
RoundRect() 関数がわかっていれば、難しくはないですね

また、多角形のリージョンを生成することも可能です
多角形のリージョンの生成には CreatePolygonRgn() を使います
HRGN CreatePolygonRgn(
	CONST POINT *lppt,
	int cPoints,
	int fnPolyFillMode
);
lppt には、多角形の各頂点が格納されているPOINT構造体の配列へのポインタ
cPoints には頂点の数、fnPolyFillMode には塗りつぶしモードを指定します
塗りつぶしモードは、SetPolyFillMode() 関数で指定する定数と同じです
戻り値は、指定した多角形のリージョンのハンドルです

予想できると思いますが、複数の多角形リージョンも生成できます
この場合は CreatePolyPolygonRgn() 関数を使用します
HRGN CreatePolyPolygonRgn(
	CONST POINT *lppt,
	CONST INT *lpPolyCounts,
	int nCount,
	int fnPolyFillMode
);
lppt には、多角形の角頂点の位置を表した配列へのポインタを
lpPolyCounts には、各多角形の頂点の数が入った配列へのポインタを指定します
nCount は多角形の総数、fnPolyFillMode は多角形の塗りつぶしモードです
戻り値は、作成されたリージョンのハンドルです
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	HDC hdc;
	PAINTSTRUCT ps;
	HRGN hrgn;
	static POINT pt[3];

	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		pt[0].x = 10 ; pt[0].y = 10;
		pt[1].x = 100 ; pt[1].y = 10;
		pt[2].x = 30 ; pt[2].y = 100;
		return 0;
	case WM_PAINT:
		hdc = BeginPaint(hwnd , &ps);
		SelectObject(hdc , GetStockObject(GRAY_BRUSH));

		hrgn = CreatePolygonRgn(pt , 3 , ALTERNATE);
		PaintRgn(hdc , hrgn);

		DeleteObject(hrgn);
		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)) DispatchMessage(&msg);
	return msg.wParam;
}


ちゃんと、多角形が描画されましたね


CreateRectRgn()

HRGN CreateRectRgn(
	int nLeftRect , int nTopRect ,
	int nRightRect , int nBottomRect
);
長方形のリージョンを作成します

nLeftRect - 左上隅のX座標を指定します
nTopRect - 左上隅のY座標を指定します
nRigthRect - 右下隅のX座標を指定します
nButtomRect - 右下隅のY座標を指定します

戻り値 - 成功するとリージョンのハンドル。失敗すると、NULL

CreateRectRgnIndirect()

HRGN CreateRectRgnIndirect(CONST RECT *lprc);
長方形のリージョンを作成します

lprc - 長方形の座標が入った RECT 構造体へのポインタを指定します

戻り値 - 成功するとリージョンのハンドル。失敗すると、NULL

CreateEllipticRgn()

HRGN CreateEllipticRgn(
	int nLeftRect , int nTopRect,
	int nRightRect , int nBottomRect
);
楕円のリージョンを作成します

nLeftRect - 左上隅のX座標を指定します
nTopRect - 左上隅のY座標を指定します
nRigthRect - 右下隅のX座標を指定します
nButtomRect - 右下隅のY座標を指定します

戻り値 - 成功するとリージョンのハンドル。失敗すると、NULL

CreateEllipticRgnIndirect()

HRGN CreateEllipticRgnIndirect(CONST RECT *lprc;); 楕円のリージョンを作成します

lprc - 長方形の座標が入った RECT 構造体へのポインタを指定します

戻り値 - 成功するとリージョンのハンドル。失敗すると、NULL

CreateRoundRect()

HRGN CreateRoundRectRgn(
	int nLeftRect , int nTopRect ,
	int nRightRect , int nBottomRect ,
	int nWidthEllipse , int nHeightEllipse 
);
角の丸い長方形のリージョンを作成します

nLeftRect - 左上隅のX座標を指定します
nTopRect - 左上隅のY座標を指定します
nRigthRect - 右下隅のX座標を指定します
nButtomRect - 右下隅のY座標を指定します
nWidthEllipse - 角の丸みの幅を指定します
nHeightEllipse - 角の丸みの高さを指定します

戻り値 - 成功するとリージョンのハンドル。失敗すると、NULL

CreatePolygonRgn()

HRGN CreatePolygonRgn(
	CONST POINT *lppt,
	int cPoints,
	int fnPolyFillMode
);
多角形のリージョンを作成します

lppt - 各頂点の座標を示す POINT 構造体の配列へのポインタを指定します
cPoints - 頂点の数を指定します
fnPoluFillMode - 多角形塗りつぶしモードを指定します

戻り値 - 成功するとリージョンのハンドル。失敗すると、NULL

塗りつぶしモードは次の定数を指定します

定数解説
ALTERNATE 交互モードを選択します
多角形の各走査行について、多角形の奇数番目の辺から
偶数番目の辺までの間の領域が塗りつぶされます
WINDING 全域モードを選択します
0 以外のワインディング値をもつすべての領域が塗りつぶされます

CreatePolyPolygonRgn()

HRGN CreatePolyPolygonRgn(
	CONST POINT *lppt,
	CONST INT *lpPolyCounts,
	int nCount,
	int fnPolyFillMode
);
複数の多角形のリージョンを作成します

lppt - 多角形の角頂点の位置を表した配列へのポインタを指定します
lpPolyCounts - 各多角形の頂点の数が入った配列へのポインタを指定します
nCount - 多角形の総数を指定します
fnPolyFillMode - 多角形の塗りつぶしモードを指定します

戻り値 - 成功するとリージョンのハンドル。失敗すると、NULL

塗りつぶしモードは次の定数を指定します

定数解説
ALTERNATE 交互モードを選択します
多角形の各走査行について、多角形の奇数番目の辺から
偶数番目の辺までの間の領域が塗りつぶされます
WINDING 全域モードを選択します
0 以外のワインディング値をもつすべての領域が塗りつぶされます

FillRgn()

BOOL FillRgn(HDC hdc , HRGN hrgn , HBRUSH hbr);

指定されたブラシを使って、指定されたリージョンを塗りつぶします

hdc - デバイスコンテキストへのハンドルを指定します
hrgn - 塗りつぶすリージョンのハンドルを指定します
hbr - ブラシのハンドルを指定します

戻り値 - 成功すると 0 以外、失敗すると 0

FrameRgn()

BOOL FrameRgn(
	HDC hdc ,
	HRGN hrgn ,
	HBRUSH hbr ,
	int nWidth , int nHeight
);
指定されたブラシを使って、指定されたリージョンの境界を描きます

hdc - デバイスコンテキストへのハンドルを指定します
hrgn - 塗りつぶすリージョンのハンドルを指定します
hbr - ブラシのハンドルを指定します
nWidth - 水平方向の境界の幅を指定します
nHeight - 垂直方向の境界の幅を指定します

戻り値 - 成功すると 0 以外、失敗すると 0

PaintRgn()

BOOL PaintRgn(HDC hdc , HRGN hrgn);

デバイスコンテキストに選択されているブラシを使って
指定されたリージョンを塗りつぶします

hdc - デバイスコンテキストへのハンドルを指定します
hrgn - 塗りつぶすリージョンのハンドルを指定します

戻り値 - 成功すると 0 以外、失敗すると 0



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