・サターン方式半透明ウィンドウを作ってみよう (99/3/4,99/5/25)

X-Window の Enlightenment などでは 、背景が透けて見える半透明ウィンドウを表示することが出来ます。こういう 半透明ウィンドウを Windows でも作れるのでしょうか?

最初は背景色を NULL_BRUSH にして、いろいろ処理をしてやれば簡単に 出来るだろうと軽く考えてましたが、NULL_BRUSH を使っても背景は 透明にはなりませんでした。NULL_BRUSH は背景を 透明にするのではなくて「再描画しないこと」を指定するものなので当然ですね。 これと同じ理由でウィンドウスタイルに WS_EX_TRANSPARENT を指定しても 駄目でした。

で、次に考えたのが SetWindowRgn を使ってみる事ですが、 SetWindowRgn はウィンドウの透明化をする事は出来ても半透明化 をする事は出来ません。しかも透明化した部分には字や絵を描画することが 出来ません。

とりあえず描画はおいといて、半透明化を先に考えてみることにしました。 SetWindowRgn を使う限りでは前にやった様な 計算による半透明化処理 を行うことは不可能です。となると、残された 方法はメッシュ半透明、俗に言う「セガサターン方式(^^;半透明」を使うことでした。

詳しくはソースをみてもらうことにして、原理としてはただメッシュ状のリージョン を作って SetWindowRgn するだけです。これで次の様な半透明 ウィンドウが出来ました。

こうして、とりあえず(偽)半透明ウィンドウは出来ました。が、今度は描画に関する 問題が生じました。つまり、このウィンドウ上で文字や絵を描くと、 メッシュ状のリージョンを指定しているため穴だらけの画像になってしまいます(^^; さらに SetWindowRgn の宿命として「死ぬほど重い、て言うか、実際死ぬ」 という問題もあります (^^; これらの問題は SetWindowRgn を使っている限り解決しませんね (-_-;

と言う訳で

「現在の Windows では半透明ウィンドウを作成するのは極めて困難」

という結論に至りました(← 駄目じゃん (^^;; )

ところで Windows 2000 の GDI+ ならば半透明処理の専用 API がある(らしい)ので 半透明のウィンドウスタイルも出来るんじゃないでしょうか。 とりあえず完全な半透明ウィンドウの実現は Windows 2000 待ちでしょうね。 もし上の方法以外で半透明処理をする方法を 知ってる方がいらっしゃいましたら是非教えて下さい。 ちなみにカレンダーとかテキストビュアー とかで「半透明」をうたっているものがありますが、あれはウィンドウを表示 する前にバックの画面をキャプチャーしているだけなので本当の半透明 ウィンドウではありません。実際、後ろのウインドウ内でアニメーション をさせたりするとそれが全く反映されませんし。

それと最後に注意なのですが、sp2 以前の NT4 の場合 BeginPath() を使うと OS 自体がクラッシュする事があるそうなので(おいおい(^^;) 下のプログラムは動かさないで下さい。


// 半透明ウィンドウ   99/3/4
// (c) tokai@fides.dti.ne.jp

#include < windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

char szClassName[] = "TOMEIWIN";

// メイン
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPreInst,
                   LPSTR lpszCmdLine, int nCmdShow)
{
    HWND hWnd;
    MSG lpMsg;
    WNDCLASS wndClass;
	
	// ウィンドウ登録
    if (!hPreInst) {
        wndClass.style = 0;
        wndClass.lpfnWndProc = WndProc;
        wndClass.cbClsExtra = 0;
        wndClass.cbWndExtra = 0;
        wndClass.hInstance = hInst;
		wndClass.hIcon = NULL;
        wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wndClass.lpszMenuName = NULL;
        wndClass.lpszClassName = szClassName;
        if (!RegisterClass(&wndClass))
            return FALSE;
    }
	
	// ウィンドウ作成
    hWnd = CreateWindow(
		szClassName,"半透明",
        WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU,
        CW_USEDEFAULT,CW_USEDEFAULT,
        300,200,NULL,NULL,hInst,NULL);
	
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
	
	// メッセージループ
    while (GetMessage(&lpMsg, NULL, 0, 0)) {
        TranslateMessage(&lpMsg);
        DispatchMessage(&lpMsg);
    }
	
    return (lpMsg.wParam);
}


// プロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
	
	static HRGN hRgn;
	HRGN hRgn2,hRgn3;
	HDC hDC;
	RECT rt;
	HPEN hPen,hOldPen;
	HBRUSH hBrush,hOldBrush;
	static HDC hBufDC;
	static HBITMAP hBufBit;
	LONG width,height,cWidth,cHeight,frameWidth,captionHeight;
	POINT point[5];
	INT nPoints;
	INT i;
	
    switch (msg) {
		
	case WM_CREATE: // 初期化	
		
		// ウィンドウサイズ取得
		GetWindowRect(hWnd,&rt);
		width = rt.right-rt.left;
		height = rt.bottom-rt.top;
		
		// クライアント領域サイズ取得
		GetClientRect(hWnd,&rt);
		cWidth = rt.right-rt.left;
		cHeight = rt.bottom-rt.top;
		
		// キャプションの高さと枠の幅
		captionHeight 
			= GetSystemMetrics(SM_CYCAPTION)
			+GetSystemMetrics(SM_CYFIXEDFRAME);
		frameWidth = GetSystemMetrics(SM_CXFIXEDFRAME);
		
		// 裏画面の作成
		hDC = GetDC(hWnd);
		hBufBit = CreateCompatibleBitmap(hDC,width,height);
		hBufDC = CreateCompatibleDC(hDC);
		SelectObject(hBufDC,hBufBit);
		ReleaseDC(hWnd,hDC);
		
		// メッシュリージョンの作成
		if(BeginPath(hBufDC)){
			for(i=-cHeight;i < width;i+=2){
				
				point[0].x = i;
				point[0].y = captionHeight;
				
				point[1].x = i+cHeight;
				point[1].y = captionHeight+cHeight;
				
				point[2].x = i+cHeight+1;
				point[2].y = captionHeight+cHeight;
				
				point[3].x = i+1;
				point[3].y = captionHeight;
				
				point[4].x = i;
				point[4].y = captionHeight;
				
				nPoints = 5;
				
				PolyPolygon(hBufDC,point,&nPoints,1); 
			}
			if(EndPath(hBufDC)) hRgn = PathToRegion(hBufDC);
		}
		
		// 枠のリージョン作成
		hRgn2 = CreateRectRgn(0,0,width,height);
		hRgn3 = CreateRectRgn(frameWidth,captionHeight,
			frameWidth+cWidth,captionHeight+cHeight);
		CombineRgn(hRgn2,hRgn2,hRgn3,RGN_DIFF);
		
		// 合成してリージョン設定
		CombineRgn(hRgn,hRgn,hRgn2,RGN_OR);
		SetWindowRgn(hWnd,hRgn,TRUE);
		DeleteObject(hRgn2);
		DeleteObject(hRgn3);
		
		// 背景塗りつぶし
		hPen = CreatePen(PS_SOLID,1,RGB(255,0,0));
		hBrush = CreateSolidBrush(RGB(255,0,0));
		hOldPen = (HPEN)SelectObject(hBufDC,hPen);
		hOldBrush = (HBRUSH)SelectObject(hBufDC,hBrush);
		Rectangle(hBufDC,-1,-1,cWidth,cHeight);
		SelectObject(hBufDC,hOldPen);
		SelectObject(hBufDC,hOldBrush);
		DeleteObject(hPen);
		DeleteObject(hBrush);
		
		break;
		
	case WM_PAINT: // 描画(再描画のみ)
		
		PAINTSTRUCT ps;
		hDC = BeginPaint(hWnd,&ps);
		
		BitBlt(hDC,ps.rcPaint.left,ps.rcPaint.top,
			ps.rcPaint.right-ps.rcPaint.left,ps.rcPaint.bottom-ps.rcPaint.top,
			hBufDC,ps.rcPaint.left,ps.rcPaint.top,SRCCOPY);
		
		ReleaseDC(hWnd,hDC);
		
		break; 
		
	case WM_CLOSE: // クローズ
		
		// 裏画面削除
		DeleteDC(hBufDC);
		DeleteObject(hBufBit);
		
		// リージョン削除
		SetWindowRgn(hWnd,NULL,TRUE);
		DeleteObject(hRgn);
		
		DestroyWindow(hWnd);
		break;
		
	case WM_DESTROY: // デストロイ
		PostQuitMessage(0);
		break;
		
	default:
		return(DefWindowProc(hWnd, msg, wp, lp));
    }
    return (0L);
}

//EOF