第346章 透明ウィンドウを作る


今回は、ウィンドウの透明化や、ウィンドウにアルファをかけて半透明にしたりする 方法について解説します。また、ある特定の色を透明化して背後が透けて見える方法についても 解説します。



左の図は、クライアント領域(実は赤で塗りつぶしてあり、これを透明化してある)が透明になって デスクトップが見えています。猫の顔と「猫でもわかるLayer」という文字は見えています。



ウィンドウを移動すると透明になっているのがもっとはっきりすると思います。 透明部分で背後に見えるアイコン等はクリックすることができます。透明というより「穴」が あいていると考えた方がよいです。



ウィンドウの透明化は簡単そうに思えますが、普通に考えてやってみるとことごとく失敗します。 ところが、Windows2000以降ではこれが簡単に実現できます。

Windows2000でマウスカーソルに陰ができてカーソル自体が浮き上がって見えていることに気が ついた人も多いでしょう。これはlayerd windowsの手法を使っています。上の図はメインウィンドウを レイヤーウィンドウにしてある部分を透明化しているのです。(このプログラムはWindows2000 以降でないと正しく動作しません)

では、具体的にどうするかというと

1.CreateWindowExの第1引数(拡張ウィンドウスタイル)にWS_EX_LAYEREDを指定する
(もしくは既存のウィンドウにSetWindowLong関数でWS_EX_LAYEREDを付け加える)
この時点でウィンドウは全く見えなくなる
2.SetLayeredWindowAttributes関数でレイヤーウィンドウの不透明度やカラーキーを設定する
と、いうようにします。
BOOL SetLayeredWindowAttributes(
  HWND hwnd,           // ウィンドウのハンドル
  COLORREF crKey,      // COLORREF値
  BYTE bAlpha,         // アルファの値
  DWORD dwFlags        // アクションフラグ
);

hwndには、レイヤーウィンドウのハンドルを指定します。

crKeyには、透明にするカラーキーのRGB値を指定します。

bAlphaには、アルファ値を指定します。0で全くの透明、255で不透明となります。

dwFlagsには、次の値のいずれか、または両方を指定します。

意味
LWA_COLORKEYcrKeyが有効
LWA_ALPHAbAlphaが有効

成功すると0以外の値が返り、失敗すると0が返ります。

この機能を利用するといろいろ面白いことができそうですね。とりあえずサンプルとして 赤色を透明にしたウィンドウを作ります。

// layer01.cpp

#define _WIN32_WINNT 0x0500

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);

char szClassName[] = "layer01";    //ウィンドウクラス
HINSTANCE hInst;
さて、冒頭で_WIN32_WINNTを0x500以上にdefineしておかないと WS_EX_LAYEREDが定義されていない、とコンパイラに文句を言われます。

WinUser.hの中で

#if(_WIN32_WINNT >= 0x0500)
#define WS_EX_LAYERED           0x00080000
#endif /* _WIN32_WINNT >= 0x0500 */
というように定義されているからです。
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
                   LPSTR lpsCmdLine, int nCmdShow)
{
    MSG msg;
    BOOL bRet;
    
    hInst = hCurInst;
    if (!InitApp(hCurInst))
        return FALSE;
    if (!InitInstance(hCurInst, nCmdShow)) 
        return FALSE;
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
        if (bRet == -1) {
            break;
        } else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return (int)msg.wParam;
}

//ウィンドウ・クラスの登録

ATOM InitApp(HINSTANCE hInst)
{
    WNDCLASSEX wc;
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;    //プロシージャ名
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInst;//インスタンス
    wc.hIcon = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hCursor = (HCURSOR)LoadImage(NULL,
        MAKEINTRESOURCE(IDC_ARROW),
        IMAGE_CURSOR,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName = NULL;    //メニュー名
    wc.lpszClassName = (LPCSTR)szClassName;
    wc.hIconSm = (HICON)LoadImage(NULL,
        MAKEINTRESOURCE(IDI_APPLICATION),
        IMAGE_ICON,
        0,
        0,
        LR_DEFAULTSIZE | LR_SHARED);

    return (RegisterClassEx(&wc));
}

//ウィンドウの生成

BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hWnd;

    hWnd = CreateWindowEx(WS_EX_LAYERED,
            szClassName,
            "猫でもわかるlayer", //タイトルバーにこの名前が表示されます
            WS_OVERLAPPEDWINDOW, //ウィンドウの種類
            CW_USEDEFAULT,    //X座標
            CW_USEDEFAULT,    //Y座標
            CW_USEDEFAULT,    //幅
            CW_USEDEFAULT,    //高さ
            NULL, //親ウィンドウのハンドル、親を作るときはNULL
            NULL, //メニューハンドル、クラスメニューを使うときはNULL
            hInst, //インスタンスハンドル
            NULL);
    if (!hWnd)
        return FALSE;
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    return TRUE;
}
ここらへんはいつもと同じですが、メインウィンドウを作るときCreateWindowEx関数の 第1引数をWS_EX_LAYEREDにしている点に注意してください。
//ウィンドウプロシージャ

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
    int id;
    HDC hdc, hdc_mem;
    HBRUSH hBrush;
    PAINTSTRUCT ps;
    char szBuf[32] = "猫でもわかるLayer";
    BITMAP bmp_info;
    HBITMAP hBmp;
    int wx, wy;

    switch (msg) {
        case WM_CREATE:
            SetLayeredWindowAttributes(hWnd, RGB(255, 0, 0), 0, LWA_COLORKEY);
            break;
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            hBrush = CreateSolidBrush(RGB(255, 0, 0));
            SelectObject(hdc, hBrush);
            ExtFloodFill(hdc, 1, 1, RGB(255, 255, 255), FLOODFILLSURFACE);
            hBmp = (HBITMAP)LoadImage(hInst, "MYBMP",IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
            GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info);
            wx = bmp_info.bmWidth;
            wy = bmp_info.bmHeight;
            hdc_mem = CreateCompatibleDC(hdc);
            SelectObject(hdc_mem, hBmp);
            BitBlt(hdc, 0, 0, wx, wy, hdc_mem, 0, 0, SRCCOPY);
            DeleteObject(hBmp);
            DeleteDC(hdc_mem);
            SetBkMode(hdc, TRANSPARENT);
            TextOut(hdc, 10, 90, szBuf, (int)strlen(szBuf)); 
            DeleteObject(hBrush);
            EndPaint(hWnd, &ps);
            break;
        case WM_CLOSE:
            id = MessageBox(hWnd,
                "終了してもよろしいですか",
                "確認",
                MB_YESNO | MB_ICONQUESTION);
            if (id == IDYES)
                DestroyWindow(hWnd);
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wp, lp));
    }
    return 0;
}
プロシージャです。

WM_CREATEメッセージが来たら、SetLayeredWindowAttributes関数でカラーキーを 設定しています。この場合「赤」をカラーキーにしています。

WM_PAINTメッセージが来たら、ExtFloodFill関数でクライアント領域全体を「赤」で 塗りつぶしています。

BOOL ExtFloodFill(
  HDC hdc,          // デバイスコンテキストハンドル
  int nXStart,      // 開始点の x 座標
  int nYStart,      // 開始点の y 座標
  COLORREF crColor, // 色
  UINT fuFillType   // 種類
);
現在のブラシを使って塗りつぶします。

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

nXStart, nYStartには、塗りつぶし開始の座標を指定します。

crColorは、RGB値を指定しますが、その意味はfuFillTypeにより異なります。

fuFillTypeには、塗りつぶしの種類を指定します。次のいずれかを指定します。

意味
FLOODFILLBORDERcrColorで指定した色が囲んでいる領域を、塗りつぶします。
FLOODFILLSURFACEcrColorで指定した色と同じ色になっている領域を、塗りつぶします。

関数が成功すると0以外が、失敗すると0が返されます。

このメインウィンドウの背景は最初WHITE_BRUSHで塗りつぶされているので白です。 そこでExtFloodFill関数で「白」部分を塗りつぶすように指定します。

次に、猫のビットマップと文字列を表示しています。

今回の知識のみでいろいろ面白いウィンドウが作れそうですね。実験してみてください。


[SDK第4部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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