レイヤード ウィンドウ

Windows アプリケーションで半透明と透明効果を使用する新しい手段

Vadim Gorokhovsky and Lou Amadio
Microsoft Corporation

January 2000
日本語版最終更新日 2000年3月2日

概要: トップレベル ウィンドウに透明と半透明効果を追加するための効率的な手段となる Microsoft(R) WindowsR 2000 の新機能を説明します。

目次

はじめに
レイヤード ウィンドウ
     レイヤード ウィンドウの使用
     ヒット テスト
     トランジション効果
     レイヤード ウィンドウの使用例
まとめ

はじめに

Microsoft(R) Windows(R) 2000 には、高い品質とスパイスの効いた UI によるエンド ユーザーの使い勝手の向上と、システムをさらに使いやすくするためのいくつかの改良が含まれています。Windows 2000 のベータ版をインストールした方は、すでにこれらの変更点のいくつかにお気づきかもしれません。たとえば、アルファブレンドされた影付きのカーソル、メニューとツールチップのフェードインやメニュー選択のフェードアウトをはじめとする新しいトランジション効果、シェルでのアルファブレンドされたイメージのドラッグなどがあります。これらの効果はすべて、Windows 2000 で導入された レイヤード ウィンドウ という新しい機能を使用することによって実現されました。

ほとんどのエンド ユーザーはスムーズなトランジション効果を期待しています。情報が目の前にポンと表示されるのは自然ではありません。テレビは、フェードとスライドを使用して、これから新しい情報が表示されることを示すという見事な仕事をやってのけています。コンピュータはまだ、これらの効果を効率的に UI に組み込むことができていません。既存の UI と映画などで見られるクールな UI とどこが違うのか、ちょっと考えてみてください。レイヤード ウィンドウは、「クールな」UI を実現するための大きなパワーをプロダクト デザイナーに与えます。

ms997507.layerwin01(ja-jp,MSDN.10).gif

図 1. Microsoft Money の上の半透明の電卓と透明な時計が画面管理を容易にします。

UI の改良においては、いくつかの微妙な変更によって大きな効果を上げることができます。たとえば、アルファ シャドーはカーソルに奥行きを与え、画面の中で目立ちやすくします。したがって、大きなモニタやマルチモニタ システムでカーソルが見つけやすくなります。あるいは、エンド ユーザーがメニュー選択をした後、メニューが消える問題を考えてみましょう。選択された項目をフェードアウトすることによって、必要な視覚的フィードバックをエンド ユーザーに与えることができます。エンド ユーザーは正しい項目が選択されたことを確認することができ、クリックしてから実際のアクションが実行されるまでの時間をさりげなく埋めることができます。

さらに、デスクトップの面積は非常に限られています。モニタの大型化とマルチモニタ システムが少しは助けになっていますが、透明と半透明効果を利用する UI があれば、この問題の軽減に役立ちます。たとえば、アルファブレンドされたシースルーの Outlook アラーム ポップアップ ウィンドウは、目立ちすぎないので、表示したままでも仕事を続けることができます。あるいは、電子メールやその他の通知を画面に表示するときに半透明や透明効果を使用すれば、ポップアップ ウィンドウでエンド ユーザーの気を散らさずにすみます。

ms997507.layerwin02(ja-jp,MSDN.10).gif

図 2. Microsoft エージェントは、Windows 2000 でついに「ペナルティ ボックス」から抜け出し、以前のようにシステム パフォーマンスに影響を及ぼすことがなくなりました。

ユーザーの操作感を高めるもう 1 つの分野として、アニメーション ウィンドウがあります。これまで、アニメーションは長方形のウィンドウ、すなわち「ペナルティ ボックス」に制限されていました。あるいは、システムに対して、アニメーション フレームの形を領域として記述しなければなりませんでした。そのため、アニメーション ウィンドウを表示すると、その下にあるウィンドウを頻繁に再描画しなければならないため、パフォーマンスの問題が発生していました。 Microsoft エージェントは、Windows 2000 でついに「ペナルティ ボックス」から抜け出し、以前のようにシステム パフォーマンスに影響を及ぼすことがなくなりました。

レイヤード ウィンドウ

Windows 2000 では、Win32 API の一部が拡張されており、その1つとしてた window style bit : WS_EX_LAYERED が導入されました。これを適切に使用すれば、複雑な形のウィンドウ、アニメーション ウィンドウ、またはアルファブレンド効果を使用するウィンドウのパフォーマンスとビジュアル効果を大幅に改善することができます。レイヤード ウィンドウの完全な実装は、Windows 2000 ベータ 3 で初めて一般に公開されました。

ウィンドウは、他のウィンドウによって切り取られた長方形として表示されます。アプリケーション内のウィンドウを円形に見せるためには、アプリケーションがウィンドウを円として描画するだけでは不十分です。システムはこのウィンドウを長方形としてヒット テストし続け、このウィンドウの下にあるウィンドウは、やはりウィンドウの長方形で切り取られて表示されます。したがって、ウィンドウは中央に円のあるグレーの長方形として表示されます。

一部のアプリケーションでは、ウィンドウが実際に表示される前に、その下のビジュアル ビットのスナップショットをとって、それらのビットとウィンドウのビットを合成することもできるでしょう。しかし、このアプローチは、マルチプロセス、マルチタスキング環境ではまったくうまくいきません。他のウィンドウがこのウィンドウの下を描画することがあるからです。アプリケーションには、そのような描画がいつ行われるのかを知る手段はなく、新しく描画されたビットを取得する方法もありません。

ms997507.layerwin03(ja-jp,MSDN.10).gif

図 3. 使用前と使用後(拡大図): アルファ シャドーがカーソルに奥行きを与え、画面上で目立ちやすくします。

Windows 95/98 および Windows NT(R) 4.0 では、丸みを帯びた吹き出しやクールな透明デジタル時計などの複雑な形状をアプリケーションで作成するための正しい方法は、 SetWindowRgn API によって形状を表すウィンドウ領域を与えることによって形状を指定することでした。ウィンドウ領域の使用には、いくつかの欠点があります。ウィンドウ領域でその形状を頻繁にアニメートさせ、たり、ウィンドウ領域がドラッグされる場合、Windows は下にあるウィンドウに再描画を要求しなければなりません。そのような場合、生成されるメッセージ トラフィックが増えるだけでなく、ウィンドウに領域が関連付けられていると、Windows が無効な領域や可視領域を割り出すために行う計算はますます高コストになります。さらに、ウィンドウ領域の使用は透明、すなわち、カラーキー効果をもたらすだけであり、半透明、すなわち、トップレベル ウィンドウをアルファブレンドする手段にはなりません。

そこでレイヤード ウィンドウの出番です。レイヤード ウィンドウは、2 つの異なるコンセプトを包含しています。すなわち、レイヤリング - ウィンドウがスプライトのような動作を示すことができることと、リダイレクション - システムが従来のウィンドウの描画を画面外のバッファにリダイレクトできることです。

レイヤード ウィンドウの使用

レイヤリングが行われるためには、ウィンドウ作成をおこなうか、GWL_EXSTYLE を指定して SetWindowLong を呼び出すことによって、WS_EX_LAYERED ビットが設定されている状態にする必要があります。次に、開発者には 2 つの選択肢があります。すなわち、WM_PAINT やその他の描画メッセージに応答することによって既存の Microsoft Win32(R) 描画パラダイムを使用するか、または、よりパワフルなレイヤリング API、UpdateLayeredWindow を利用するかになります。

UpdateLayeredWindow を使用するには、レイヤード ウィンドウのビジュアル ビットが互換性のあるビットマップにレンダリングされなければなりません。次に、互換性のある GDI Device Context によって、そのビットマップが UpdateLayeredWindow API に、望ましいカラーキーとアルファブレンド情報とともに与えられます。ビットマップはピクセル単位のアルファ情報を含むこともできます。

UpdateLayeredWindow を使用するときには、アプリケーションは WM_PAINT やその他の描画メッセージに応答する必要がないことに注目してください。なぜなら、アプリケーションはすでにウィンドウの視覚表現を与えており、そのイメージの格納、合成、および画面へのレンダリングはシステムが行うからです。 UpdateLayeredWindow は非常にパワフルですが、既存の Win32 アプリケーションの描画方法の変更を必要とすることがしばしばあります。

レイヤード ウィンドウを使用する 2 番目の方法は、今までどおりに Win32 描画パラダイムを使用しますが、システムがレイヤード ウィンドウとその子ウィンドウの描画のすべてを画面外のビットマップにリダイレクトできるようにすることです。このためには、望ましい定数のアルファ値やカラーキーを指定して SetLayeredWindowAttributes を呼び出します。API が呼び出されると、システムはウィンドウによるすべての描画のリダイレクトを開始して、指定された効果を自動的に適用します。

ms997507.layerwin04(ja-jp,MSDN.10).gif

図 4. Windows 2000 のシェルはレイヤード ウィンドウの半透明と透明効果を使用して、デスクトップでのイメージ ドラッグの品質を高めます。

UpdateLayeredWindow 属性を使用した方が効率的な場合もあります。アプリケーションはすべてのレイヤード ウィンドウのメモリ ビットマップを格納する必要がないかもしれないからです。アプリケーションは 1 つのメモリ ビットマップを保持して、複数のレイヤード ウィンドウについて UpdateLayeredWindow を呼び出す直前に、そのビットマップに描画することができます。アプリケーションが UpdateLayeredWindow を呼び出す頻度に応じて、API を呼び出した後でビットマップを削除することもできます。一方、システムによってリダイレクトされたウィンドウは常に、リダイレクトされたウィンドウのそれぞれについて、ウィンドウ サイズのメモリ ビットマップを保持しなければならないという負担を負うことになります。これは、 UpdateLayeredWindow を使用してレイヤード ウィンドウを表示したときに消費されるメモリへの負担増となります。このため、確かに便利ではありますが、 SetLayeredWindowAttributes の使用には代償が伴います。高速なアニメーションを使用する場合は、 UpdateLayeredWindow を使用すべきです。

また、 SetLayeredWindowAttributes がレイヤード ウィンドウに対して呼び出された後、レイヤリング スタイル ビットがクリアされて、再び設定されるまでは、後続の UpdateLayeredWindow コールは失敗するということを忘れないでください。これは、 SetLayeredWindowAttributes がウィンドウ描画のリダイレクトを有効にするためであり、その場合、 UpdateLayeredWindow はウィンドウが実際にはどのように見えるかについて矛盾した情報を与えることがあるからです。

ヒット テスト

レイヤード ウィンドウのヒット テストは、ウィンドウの形状と透明度に基づいています。これは、色分けされているウィンドウの領域やアルファ値がゼロの領域は、マウス メッセージが透過することを意味します。

レイヤード ウィンドウが WS_EX_TRANSPARENT 拡張ウィンドウ スタイルを持つ場合、レイヤード ウィンドウの形状は無視され、マウス イベントはレイヤード ウィンドウの下の他のウィンドウに渡されます。

ms997507.layerwin05(ja-jp,MSDN.10).gif

図 5. [スタート] メニューが消えた後、選択されたスタート メニュー項目 [ファイル名を指定して実行] がフェードアウトします。

トランジション効果

ウィンドウをフェードインまたはフェードアウトするために大量のコードを書く必要はありません。 AnimateWindow API が、あなたの代わりにすべての仕事をやってくれます。実際、このようにして、シェルの [スタート] メニューとその他のメニューはオープン フェードイン効果を出しています。

内部で、 AnimateWindow がウィンドウを重ね合わせて、目的のトランジション効果を与えます。 AnimateWindow はリダイレクション機能を使用してウィンドウのイメージを得ることもできますが、現時点では WM_PRINT メッセージを使用しています。

フェードのほかに、 Animate Window はスライド効果を作ることもできます。実際、カスタム メニューを書く場合は、この API が非常に役に立ちます。あなたのアプリケーションが、良いデスクトップ市民であるためには、メニューを表示するときにメニューをアニメーション表示するかどうかを判断しなければなりません。その情報を得るには、SPI_GETMENUANIMATION を使用して、 SystemParametersInfo によってシステムに問い合わせます。さらに、SPI_GETMENUFADE を使用して、スライドとフェードのいずれのアニメーション効果を使用すべきかどうかを調べることができます。使用すべき効果がわかったら、スライド効果を得るには AW_SLIDE を、フェード効果を得るには AW_BLEND を AnimateWindow に渡します。

AnimateWindow には、トランジションの時間の長さを指定するパラメータもあります。常識的には、トランジション効果は 200 ミリ秒以下にすべきです。

パワー ユーザーの中には、レイヤード ウィンドウによって有効にされるトランジション効果のいくつかが気に入らず、それらを無効にしたいと思う人もいるでしょう。このため、アプリケーションは行儀よくして、 SystemParametersInfo によってシステムに問い合わせて、効果が有効になっているかどうかを確認することが重要です。

レイヤード ウィンドウの使用例

ダイアログ ボックスを半透明のウィンドウとして表示したい場合は、次のようにします。

  1. いつものようにダイアログ ボックスを作成します。

  2. WM_INITDIALOG で、ウィンドウの拡張スタイルのレイヤード ビットを設定して、望ましいアルファ値を指定して SetLayeredWindowAttributes を呼び出します。

コードは次のようになります。

// このウィンドウに WS_EX_LAYERED を設定する 
SetWindowLong(hwnd, GWL_EXSTYLE,
        GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);
// このウィンドウを 70% のアルファにする
SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);

SetLayeredWindowAttributes の 3 番目のパラメータは 0 から 255 までの値であり、0 は完全な透明、255 は完全な不透明を意味します。このパラメータは、より多用途な AlphaBlend API の BLENDFUNCTION を模倣しています。

このウィンドウを再び完全に不透明にしたい場合は、 SetWindowLong を呼び出すことによって WS_EX_LAYERED を削除した後、ウィンドウに再描画を要求します。ビットの削除は、レイヤリングとリダイレクトのためのメモリを解放してもよいことをシステムに知らせるために必要です。コードは、次のようになります。

// このウィンドウ スタイルから WS_EX_LAYERED を削除する
SetWindowLong(hwnd, GWL_EXSTYLE,
        GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
// ウィンドウとその子に再描画を要求する
RedrawWindow(hwnd, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_FRAME |
        RDW_ALLCHILDREN);

カスタム メニューの選択フェードアウトなど、ウィンドウの特定の領域をフェードアウトするには、この仕事を行うために UpdateLayeredWindow または SetLayeredWindowAttributes を使用する専用のレイヤード ウィンドウを作成します。たとえば、次の関数 - シェルがメニュー選択項目をフェードアウトするときに行うことと同様 - でレイヤード ウィンドウを作成して、画面上の長方形によって定義される初期ビジュアル ビットをシステムに与えます。

VOID FadeRect(RECT* prc, HDC hdc)
{
    BOOL fFade = FALSE;
    HWND hwnd;
    SIZE size;
    POINT ptSrc = {0, 0};
    BLENDFUNCTION blend;
 
    // ユーザーの意志を尊重せよ: 彼らはフェードを望んでいるか?
    SystemParametersInfo(SPI_GETSELECTIONFADE, 0, &fFade, 0);
    if (!fFade)
        return;
    hwnd = CreateWindowEx(WS_EX_LAYERED | // レイヤード ウィンドウ
            WS_EX_TRANSPARENT | // このウィンドウをヒット テストしない
            WS_EX_TOPMOST | WS_EX_TOOLWINDOW, 
            gszFade, gszFade, WS_POPUP | WS_VISIBLE, prc->left,
            prc->top, 0, 0, NULL, (HMENU)0, ghinst, NULL);
    size.cx = prc->right - prc->left;
    size.cy = prc->bottom - prc->top;
    
    blend.BlendOp = AC_SRC_OVER;
    blend.BlendFlags = 0;
    blend.AlphaFormat = 0;
    blend.SourceConstantAlpha = gbAlpha;
 
    UpdateLayeredWindow(hwnd, NULL, NULL, &size, hdc, &ptSrc, 0,
            &blend, ULW_ALPHA);
    // 最後にアニメーション タイマを設定する
    SetTimer(hwnd, ID_TIMER, 25, NULL);
}

その後、タイマに従って、新しいアルファ値を与えることによってウィンドウはフェードアウトします。

    case WM_TIMER:
        {
            BLENDFUNCTION blend;
            blend.BlendOp = AC_SRC_OVER;
            blend.BlendFlags = 0;
            blend.AlphaFormat = 0;
            blend.SourceConstantAlpha = gbAlpha;
 
            UpdateLayeredWindow(hwnd, NULL, NULL, NULL, NULL, NULL,
                    NULL, &blend, ULW_ALPHA);
            if (gbAlpha > 25) {
                gbAlpha -= 25;
            } else {
                DestroyWindow(hwnd);
            }
        }
        break;

まとめ

レイヤード ウィンドウは、トップレベル ウィンドウに透明と半透明効果を追加する効率的な手段となります。レイヤード ウィンドウによって、開発者は斬新な UI や気の利いたトランジション効果を新しいアプリケーションや既存のアプリケーションに容易に組み込むことができます。