EternalWindows
リージョン / テキスト型のリージョン

リージョン関数は、長方形や楕円といった一定の形の作成をサポートしますが、 今回のようなテキストの形をしたリージョンの作成はサポートしていません。 そこで、今回はパスというものを通じて、テキスト型のリージョンを作成したいと思います。 パスというのは、デバイスコンテキスト属性の1つであり、 描画すべき図形を構成する点を定義することができます。 パスを開いた場合、GDI関数の呼び出しは実際にデバイスに描画されることはなく、 単に図形の点がパスとして定義されることになります。 パスを開くには、BeginPathを呼び出します。

BOOL BeginPath(
  HDC hdc
);

hdcは、デバイスコンテキストのハンドルを指定します。

開いたパスを閉じた場合、そのパスはデバイスコンテキストに選択され、 パスがデバイスコンテキストの属性として有効になります。 また、GDI関数の呼び出しは実際にデバイスに描画されるようになります。 パスを閉じるには、EndPathを呼び出します。

BOOL EndPath(
  HDC hdc
);

hdcは、デバイスコンテキストのハンドルを指定します。

パスによって定義された点は、PathToRegionでリージョンに変換することができます。

HRGN PathToRegion(
  HDC hdc
);

hdcは、デバイスコンテキストのハンドルを指定します。 戻り値は、リージョンのハンドルになります。

次に、パス関数の呼び出しの流れを示します。

BeginPath(hdc);

Rectangle(hdc, 0, 0, 100, 100);

EndPath(hdc);

hrgn = PathToRegion(hdc);

まず、BeginPathでパスを開き、次にGDI描画関数を呼び出します。 既に述べたように、この呼び出しは実際にデバイスに描画されることはなく、 パスとして定義されるだけです。 GDI描画関数の呼び出しを終えたらEndPathを呼び出し、 最後にPathToRegionでリージョンに変換します。 上記では、使用している関数がRectangleであるため、長方形のリージョンを取得することになりますが、 テキストを描画する関数を呼び出せば、テキスト型のリージョンを取得することができます。

今回のプログラムは、テキスト型のリージョンを作成して描画します。 また、リージョンがマウスで押下された場合はメッセージを表示します。

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HRGN hrgn = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		HDC     hdc;
		HFONT   hfont;
		HFONT   hfontPrev;
		LOGFONT lf;
		
		ZeroMemory(&lf, sizeof(LOGFONT));
		lf.lfHeight  = 80;
		lf.lfCharSet = ANSI_CHARSET;
		lstrcpy(lf.lfFaceName, TEXT("Times New Roman"));

		hfont = CreateFontIndirect(&lf);
		if (hfont == NULL)
			return -1;

		hdc = GetDC(hwnd);
		hfontPrev = (HFONT)SelectObject(hdc, hfont);

		BeginPath(hdc);
		SetBkMode(hdc, TRANSPARENT);
		TextOut(hdc, 30, 30, TEXT("EternalWindows"), lstrlen(TEXT("EternalWindows")));
		EndPath(hdc);
		hrgn = PathToRegion(hdc);
		
		ReleaseDC(hwnd, hdc);
		DeleteObject(hfont);

		return hrgn != NULL ? 0 : -1;
	}

	case WM_LBUTTONDOWN: {
		int x, y;

		x = LOWORD(lParam);
		y = HIWORD(lParam);

		if (PtInRegion(hrgn, x, y))
			MessageBox(hwnd, TEXT("リージョンを選択しました。"), TEXT("OK"), MB_OK);;

		return 0;
	}

	case WM_PAINT: {
		HDC         hdc;
		PAINTSTRUCT ps;

		hdc = BeginPaint(hwnd, &ps);

		FillRgn(hdc, hrgn, (HBRUSH)GetStockObject(GRAY_BRUSH));

		EndPaint(hwnd, &ps);

		return 0;
	}

	case WM_DESTROY:
		if (hrgn != NULL)
			DeleteObject(hrgn);
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

テキスト型のリージョンは、WM_CREATEで作成しています。 まず、テキストをできるだけ大きくするためにフォントを作成し、 デバイスコンテキストに予め選択しておきます。 BeginPathを呼び出したら、SetBkModeでテキストを透過モードで描画するように指示し、 テキストを描画するTextOutを呼び出します。 後は、EndPathを呼び出てPathToRegionを呼び出せば、テキスト型のリージョンを取得できたことになります。

テキストの描画にはDrawTextという関数もありますが、この関数は使用することができません。 パスと関連のある描画関数は決まっており、DrawTextはその中の1つではないのです。 また、Windowsのバージョンによっては、使用できない描画関数もあるので注意してください。


戻る