第48章 クリップリージョン


まずは、下の図を見てください。

もともとは、長方形のビットマップなのですが 表示する部分をクリップしたのです。閉じた図形の 占める領域をリージョンといいます。クリップリージョンを 作るにはまず、リージョンを作ります。そしてこれを デバイスコンテキストのクリッピング属性に割り当てれば クリップリージョンができます。複数のリージョンを組み合わせて 複雑なリージョンを作ることもできます。

上の図を見ると想像がつきますね。左は楕円形のリージョンを 作ってこれをクリップリージョンにしています。真ん中は、2つの リージョンをたしあわせて作ったものです。右は、同様なリージョンの 組み合わせですが、重なり合った部分を除外しています。

リージョンを作る関数はCreate**Rgnという名前になります。 **の部分に図形の名前がきます。四角形のリージョンを 作るにはCreateRectRgnという具合です。

HRGN CreateRectRgn( int nLeftRect, int nTopRect, int nRightRect, int nBottomRect );

これは、長方形のリージョンを作ります。

HRGN CreateEllipticRgn( int nLeftRect, int nTopRect, int nRightRect, int nBottomRect );

これは、楕円のリージョンを作ります。(4つの引数で作られる長方形の内部に 収まる楕円) これらの関数が成功するとリージョンハンドルを取得することができます。 そして、SelectClipRgn関数を実行するとクリッピングリージョンになります。 あとは、普通に描画すればクリップした領域にしか描画されません。

int SelectClipRgn( HDC hdc, // デバイスコンテキストハンドル HRGN hrgn // リージョンハンドル );

この関数の戻り値はNULLREGION, SIMPLEREGION, COMPLEXREGION, ERRORの いずれかになります。ヘルプで調べてみてください。 またリージョンハンドルは使い終わったらDeleteObjectで削除します。

リージョンの結合には、CombineRgn関数を使います。

int CombineRgn( HRGN hrgnDest, // 新しく作られるリージョン HRGN hrgnSrc1, // 元になるリージョン1 HRGN hrgnSrc2, // 元になるリージョン2 int fnCombineMode // 結合の仕方 );

あらかじめ適当なリージョンhrgnDestを作っておきます。(何でもよい)そして、 結合したいリージョンを2番目と3番目の引数にします。 結合の仕方は、RGN_AND重なった部分のみ)、 RGN_OR (両方の領域の和)、 RGN_XOR(重なった領域はのぞく)、 RGN_COPY(リージョン1のみ)、 RGN_DIFF(リージョン1からリージョン2の部分を取り除く)の5つがあります。 では、さっそくプログラムを見てみましょう。

// rgn01.rc ///////////////////////////////////////////////////////////////////////////// // // Bitmap // MYBMP BITMAP DISCARDABLE "mybmp.bmp" ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "オプション(&O)" BEGIN MENUITEM "そのまま表示(&R)", IDM_RCT MENUITEM "楕円クリップ(&E)", IDM_ELL MENUITEM "ORクリップ(&O)", IDM_ELLOR MENUITEM "XORクリップ(&X)", IDM_ELLXOR END MENUITEM "終了(&X)", IDM_END END

自分でリソーススクリプトを書く人は、windows.hやシンボル定義のヘッダーファイルの インクルードを忘れないでください。

// rgn01.cpp #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); void DrawMyBMP(HWND, HDC, int); HRGN MakeMyClipRgn(int, BITMAP); char szClassName[] = "rgn01"; //ウィンドウクラス int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; if (!hPrevInst) { if (!InitApp(hCurInst)) return FALSE; } if (!InitInstance(hCurInst, nCmdShow)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるリージョン", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 195, //幅 163, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } //ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { static int sw; //クリップリージョンの形 int id; HDC hdc; PAINTSTRUCT ps; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_RCT: sw = 1; InvalidateRect(hWnd, NULL, TRUE); break; case IDM_ELL: sw = 2; InvalidateRect(hWnd, NULL, TRUE); break; case IDM_ELLOR: sw = 3; InvalidateRect(hWnd, NULL, TRUE); break; case IDM_ELLXOR: sw = 4; InvalidateRect(hWnd, NULL, TRUE); break; case IDM_END: SendMessage(hWnd, WM_CLOSE, 0L, 0L); break; } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); DrawMyBMP(hWnd, hdc, sw); EndPaint(hWnd, &ps); break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; } void DrawMyBMP(HWND hWnd, HDC hdc, int sw) { HINSTANCE hInst; HBITMAP hBitmap; BITMAP bm; HDC hmdc; LONG bmp_w, bmp_h; HRGN hRgn; hInst = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); hBitmap = LoadBitmap(hInst, "MYBMP"); GetObject(hBitmap, sizeof(BITMAP), &bm); bmp_w = bm.bmWidth; bmp_h = bm.bmHeight; hRgn = MakeMyClipRgn(sw, bm); SelectClipRgn(hdc, hRgn); hmdc = CreateCompatibleDC(hdc); SelectObject(hmdc, hBitmap); BitBlt(hdc, 0, 0, (int)bmp_w, (int)bmp_h, hmdc, 0, 0, SRCCOPY); DeleteObject(hRgn); DeleteDC(hmdc); DeleteObject(hBitmap); return; } HRGN MakeMyClipRgn(int sw, BITMAP bitmap) { int bmp_w, bmp_h; bmp_w = (int)bitmap.bmWidth; bmp_h = (int)bitmap.bmHeight; HRGN hRgn, hRgn1, hRgn2; switch (sw) { case 1: return CreateRectRgn(0, 0, bmp_w, bmp_h); case 2: return CreateEllipticRgn(0, 0, bmp_w, bmp_h); case 3: hRgn = CreateRectRgn(0, 0, 10, 10); hRgn1 = CreateEllipticRgn(0, 0, (bmp_w / 3)*2, bmp_h); hRgn2 = CreateEllipticRgn(bmp_w / 3, 0, bmp_w, bmp_h); CombineRgn(hRgn, hRgn1, hRgn2, RGN_OR); DeleteObject(hRgn1); DeleteObject(hRgn2); return hRgn; case 4: hRgn = CreateRectRgn(0, 0, 10, 10); hRgn1 = CreateEllipticRgn(0, 0, (bmp_w / 3)*2, bmp_h); hRgn2 = CreateEllipticRgn(bmp_w / 3, 0, bmp_w, bmp_h); CombineRgn(hRgn, hRgn1, hRgn2, RGN_XOR); DeleteObject(hRgn1); DeleteObject(hRgn2); return hRgn; default: return NULL; } }

自作関数のMakeMyClipRgnをよく見てください。 メニューが選択されるとswに値がセットされます。 この値を見てどういうリージョンを作るかを場合分けしています。 なお、この関数の名前の付け方失敗です。クリップリージョンを 作っているのではなく単なるリージョンを作っているのでMakeMyRgnとでも しておけばよかったですね。(どうでもいいことですが・・)


[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Jun/18/1997 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。