最終更新:2015-04-06 (月) 14:59:54 (3280d)
UpdateLayeredWindow
Top / UpdateLayeredWindow
32ビットのARGBビットマップ(透過PNGとか)からレイヤードウィンドウを生成
CreateWindowEx 関数を使ってウィンドウを作成するとき、WS_EX_LAYERED フラグをセットすると、作成されるウィンドウはレイヤードウィンドウになります。
もしくはSetWindowLongで変える。
http://blogs.wankuma.com/youryella/archive/2007/10/15/102151.aspx
定義
BOOL UpdateLayeredWindow( HWND hwnd, // レイヤードウィンドウのハンドル HDC hdcDst, // 画面のデバイスコンテキストのハンドル POINT *pptDst, // 画面の新しい位置 SIZE *psize, // レイヤードウィンドウの新しいサイズ HDC hdcSrc, // サーフェスのデバイスコンテキストのハンドル POINT *pptSrc, // レイヤの位置 COLORREF crKey, // カラーキー BLENDFUNCTION *pblend, // ブレンド機能 DWORD dwFlags // フラグ );
メモ
- UpdateLayeredWindow 関数を呼び出したために現れる、レイヤーウィンドウの下のウィンドウは再描画する必要がありません。システムによって再描画されるためです。そのため、レイヤードウィンドウのシームレスなアニメーションが可能です。
- The UpdateLayeredWindow function maintains the window's appearance on the screen. The windows underneath a layered window do not need to be repainted when they are uncovered due to a call to UpdateLayeredWindow, because the system will automatically repaint them. This permits seamless animation of the layered window.
- UpdateLayeredWindow always updates the entire window. To update part of a window, use the traditional WM_PAINT and set the blend value using SetLayeredWindowAttributes.
C♯
using System; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms; using System.Runtime.InteropServices; // UpdateLayeredWindow用ユーティリティ class ULW { public enum Bool { False= 0, True }; [StructLayout(LayoutKind.Sequential)] public struct Point { public Int32 x; public Int32 y; public Point(Int32 x, Int32 y) { this.x= x; this.y= y; } } [StructLayout(LayoutKind.Sequential)] public struct Size { public Int32 cx; public Int32 cy; public Size(Int32 cx, Int32 cy) { this.cx= cx; this.cy= cy; } } [StructLayout(LayoutKind.Sequential, Pack=1)] struct ARGB { public byte Blue; public byte Green; public byte Red; public byte Alpha; } [StructLayout(LayoutKind.Sequential, Pack=1)] public struct BLENDFUNCTION { public byte BlendOp; public byte BlendFlags; public byte SourceConstantAlpha; public byte AlphaFormat; } public const Int32 ULW_COLORKEY = 0x00000001; public const Int32 ULW_ALPHA = 0x00000002; public const Int32 ULW_OPAQUE = 0x00000004; public const byte AC_SRC_OVER = 0x00; public const byte AC_SRC_ALPHA = 0x01; public static int GWL_EXSTYLE = -20; public static int WS_EX_LAYERED = 0x80000; public static int LWA_ALPHA = 0x2; [DllImport("user32.dll", ExactSpelling=true, SetLastError=true)] public static extern Bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags); [DllImport("user32.dll", ExactSpelling=true, SetLastError=true)] public static extern IntPtr GetDC(IntPtr hWnd); [DllImport("user32.dll", ExactSpelling=true)] public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC); [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] public static extern IntPtr CreateCompatibleDC(IntPtr hDC); [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] public static extern Bool DeleteDC(IntPtr hdc); [DllImport("gdi32.dll", ExactSpelling=true)] public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject); [DllImport("gdi32.dll", ExactSpelling=true, SetLastError=true)] public static extern Bool DeleteObject(IntPtr hObject); [DllImport("user32")] public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); [DllImport("user32")] public static extern int GetWindowLong(IntPtr hWnd, int nIndex); /// <para>Changes the current bitmap.</para> static public void SetULW(Form form, Bitmap bitmap) { SetULW(form,bitmap, 255); } /// <para>Changes the current bitmap with a custom opacity level. Here is where all happens!</para> static public void SetULW(Form form,Bitmap bitmap, byte opacity) { if (bitmap.PixelFormat != PixelFormat.Format32bppArgb) throw new ApplicationException("The bitmap must be 32ppp with alpha-channel."); // The ideia of this is very simple, // 1. Create a compatible DC with screen; // 2. Select the bitmap with 32bpp with alpha-channel in the compatible DC; // 3. Call the UpdateLayeredWindow. IntPtr screenDc = GetDC(IntPtr.Zero); IntPtr memDc = CreateCompatibleDC(screenDc); IntPtr hBitmap = IntPtr.Zero; IntPtr oldBitmap = IntPtr.Zero; try { hBitmap = bitmap.GetHbitmap(Color.FromArgb(0)); // grab a GDI handle from this GDI+ bitmap oldBitmap = SelectObject(memDc, hBitmap); Size size = new Size(bitmap.Width, bitmap.Height); Point pointSource = new Point(0, 0); Point topPos = new Point(form.Left, form.Top); BLENDFUNCTION blend = new BLENDFUNCTION(); blend.BlendOp = AC_SRC_OVER; blend.BlendFlags = 0; blend.SourceConstantAlpha = opacity; blend.AlphaFormat = AC_SRC_ALPHA; SetWindowLong(form.Handle, GWL_EXSTYLE, GetWindowLong(form.Handle, GWL_EXSTYLE) | WS_EX_LAYERED); UpdateLayeredWindow(form.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, 0, ref blend, ULW_ALPHA); } finally { ReleaseDC(IntPtr.Zero, screenDc); if (hBitmap != IntPtr.Zero) { SelectObject(memDc, oldBitmap); //Windows.DeleteObject(hBitmap); // The documentation says that we have to use the Windows.DeleteObject... but since there is no such method I use the normal DeleteObject from ULWUtility GDI and it's working fine without any resource leak. DeleteObject(hBitmap); } DeleteDC(memDc); } } }
VB.NET
Imports System.Drawing Imports System.Runtime.InteropServices Public Class LayeredForm #Region " UpdateLayerdWindow 関連 API " <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)> _ Public Shared Function CreateCompatibleDC(ByVal hDC As IntPtr) As IntPtr End Function <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)> _ Public Shared Function DeleteDC(ByVal hdc As IntPtr) As Boolean End Function <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)> _ Private Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean End Function <DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)> _ Private Shared Function SelectObject(ByVal hDC As IntPtr, ByVal hObject As IntPtr) As IntPtr End Function <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)> _ Private Shared Function GetDC(ByVal hWnd As IntPtr) As IntPtr End Function <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)> _ Private Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Integer End Function <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)> _ Private Shared Function UpdateLayeredWindow( _ ByVal hwnd As IntPtr, _ ByVal hdcDst As IntPtr, _ <System.Runtime.InteropServices.In()> _ ByRef pptDst As Point, _ <System.Runtime.InteropServices.In()> _ ByRef psize As Size, _ ByVal hdcSrc As IntPtr, _ <System.Runtime.InteropServices.In()> _ ByRef pptSrc As Point, _ ByRef crKey As Integer, _ <System.Runtime.InteropServices.In()> _ ByRef pblend As BLENDFUNCTION, _ ByVal dwFlags As Integer _ ) As Boolean End Function <StructLayout(LayoutKind.Sequential, Pack:=1)> _ Private Structure BLENDFUNCTION Public BlendOp As Byte Public BlendFlags As Byte Public SourceConstantAlpha As Byte Public AlphaFormat As Byte End Structure Private Const WS_EX_LAYERED As Integer = &H80000 Private Const WS_BORDER As Integer = &H800000 Private Const WS_THICKFRAME As Integer = &H40000 Private Const AC_SRC_OVER As Byte = 0 Private Const AC_SRC_ALPHA As Byte = 1 Private Const ULW_ALPHA As Integer = 2 #End Region #Region " コンストラクタ " Public Sub New(ByVal bmp As Bitmap) ' この呼び出しは、Windows フォーム デザイナで必要です。 InitializeComponent() ' InitializeComponent() 呼び出しの後で初期化を追加します。 Me.SetBackground(bmp) End Sub #End Region #Region " オーバーライド " Protected Overrides ReadOnly Property CreateParams() As CreateParams Get ' レイヤードウィンドウスタイルを適用 Dim cp As CreateParams = MyBase.CreateParams cp.ExStyle = cp.ExStyle Or WS_EX_LAYERED cp.Style = cp.Style And (Not WS_BORDER) cp.Style = cp.Style And (Not WS_THICKFRAME) Return cp End Get End Property #End Region #Region " Public メソッド " Public Sub SetBackground(ByVal srcBitmap As Bitmap) ' デバイスコンテキストを取得 Dim screenDc As IntPtr = GetDC(IntPtr.Zero) Dim memDc As IntPtr = CreateCompatibleDC(screenDc) Dim hBitmap As IntPtr = IntPtr.Zero Dim hOldBitmap As IntPtr = IntPtr.Zero Try hBitmap = srcBitmap.GetHbitmap(Color.FromArgb(0)) hOldBitmap = SelectObject(memDc, hBitmap) ' BLENDFUNCTION を初期化 Dim blend As New BLENDFUNCTION blend.BlendOp = AC_SRC_OVER blend.BlendFlags = 0 blend.SourceConstantAlpha = 255 blend.AlphaFormat = AC_SRC_ALPHA ' レイヤードウィンドウを更新 Dim r As Boolean = UpdateLayeredWindow( _ Me.Handle, screenDc, Me.Location, New Size(srcBitmap.Width, srcBitmap.Height), _ memDc, New Point(0, 0), 0, blend, ULW_ALPHA _ ) Finally ReleaseDC(IntPtr.Zero, screenDc) If hBitmap <> IntPtr.Zero Then SelectObject(memDc, hOldBitmap) DeleteObject(hBitmap) End If DeleteDC(memDc) End Try End Sub #End Region End Class Dim f As New LayeredForm(New Bitmap("hoge.png")) f.Show()
Delphi
interface function UpdateLayeredWindow(_hwnd:HWND; dstHDC:HDC; pptDst:PPoint; ASize:PSize; srcHDC:HDC; pptSrc:PPoint; crKey:COLORREF; var bf : BLENDFUNCTION; dwFlag:DWORD):BOOL; stdcall; function SetULW(Handle:HWND;BMP:TBitMap;Alpha:Integer):boolean; const WS_EX_LAYERED = $80000; LWA_COLORKEY = 1; LWA_ALPHA = 2; ULW_COLORKEY = 1; ULW_ALPHA = 2; ULW_OPAQUE = 4; AC_SRC_ALPHA = 1; implementation function UpdateLayeredWindow; external 'user32.dll' name 'UpdateLayeredWindow'; function SetULW(Handle:HWND;BMP:TBitMap;Alpha:Integer):boolean; var bf : TBlendFunction; zerop : TPoint; formsz : TSize; begin result:=false; WITH bf DO BEGIN BlendOp:=AC_SRC_OVER; BlendFlags:=0; SourceConstantAlpha:=Alpha; // 完全にALPHAをBITMAPに依存する場合 AlphaFormat:=AC_SRC_ALPHA; END; zerop.x:=0; zerop.y:=0; formsz.cx:=BMP.Width; formsz.cy:=BMP.height; SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE)Xor WS_EX_LAYERED); SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_LAYERED); IF NOT UpdateLayeredWindow( handle, 0, nil, // palleteを気にしないならnilでよろし @formsz, // フォームの大きさの指定 : 必須! BMP.canvas.handle, // サーフェイスを定義するDC @zerop, // サーフェイスを定義する画像の開始点 0, bf, ULW_ALPHA) THEN BEGIN ShowMessage(SysErrorMessage(GetLastError)); Exit; END; result:=true; end;
Windows 7 におけるULWの挙動
- http://c299792458.hatenablog.com/entry/20100417/1271497243
- http://c299792458.hatenablog.com/entry/20100418/1271567992
- http://c299792458.hatenablog.com/entry/20100418/1271570052
Windows 7 での滑らかなアニメーションの実現
- http://c299792458.hatenablog.com/entry/20100604/1275663058
- http://c299792458.hatenablog.com/entry/20100606/1275805530
- http://c299792458.hatenablog.com/entry/20100606/1275806798
メモ
- 裏に入ったウインドウの描画まで平均的に遅くなる
- 完全に裏に入っていなくても不可視領域にカスっただけで遅くなる
- 大元の資料にすら「Layered Window はできるだけ面積を小さくして下さい」等とえらく弱気な説明が書かれている
- http://usada.sakura.vg/contents/specification2.html