- PR -

アルファ付きの画像とデスクトップを綺麗に重ねるには?

1
投稿者投稿内容
toto
会議室デビュー日: 2007/09/15
投稿数: 4
投稿日時: 2007-09-15 13:11
Rainmeterのような、スキンの設定できるデスクトップアクセサリを作ろうとしています。やりたいことは、アルファ付きのpng画像をデスクトップと綺麗に重ねることです。

フォームのBackcolorを適当に設定し、それをTransparencyKeyにして、BackgroundImageにアルファ付きpngを貼ったところ、pngの完全に透明のところは透明になってデスクトップが透過するのですが、半透明のところはフォームの背景色と重なってしまいました。つまり半透明部分はデスクトップが透過しません。
BackgroundImageではなくPictureBoxに貼るようにしても同じ結果です。

フォームのBackcolorにTransparentが設定できればいいのかな、と思えるのですが、設定しようとすると”プロパティの値が無効です”というエラーになります。

Rainmeterではアルファ付きpngをデスクトップに重ねて表示できているので、Windowsのシステム的にはできると思うのですが、識者の方の教えを頂きたく。よろしくおねがいします。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-09-15 19:43
フォーム自身を透明にすればいいのでは?
toto
会議室デビュー日: 2007/09/15
投稿数: 4
投稿日時: 2007-09-15 19:56
回答ありがとうございます。
フォームのOpacityを設定するという意味ですよね?
でもそうすると、フォームの上にあるもの全体に同じアルファがかかってしまうと
思うのですが、どうでしょうか。
画像のアルファチャンネルを使って、ピクセル単位でアルファを設定したいのです。
toto
会議室デビュー日: 2007/09/15
投稿数: 4
投稿日時: 2007-09-15 21:58
自己レスですが、
フォームの後ろにあるデスクトップ画像をキャプチャしてフォームのBackgrowndImageに貼る、という強引なやり方で、目的に近いことはできるようになりました。

難点は、後ろの画像が変化するとキャプチャしなおす必要があり、paintイベントでやってると負荷が気になることです。タイマーで0.5秒おきにキャプチャしたりということを試しています。

もっと素直な方法があるといいのですが。
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2007-09-15 22:26
引用:

totoさんの書き込み (2007-09-15 21:58) より:
もっと素直な方法があるといいのですが。



素直でないような気もしますが、
この間遊びで作ったものを張っておきます。

TransparencyKeyはバグもちで、(http://support.microsoft.com/kb/822495/ja)
解消のためにごにょるのはめんどくさいですし、
特定の色を透明にしたいことより、透明なpngやgifを読みたいことが多いので、
私は使いません。

用途よってRegionかLayeredを使い分けてます。
今回はアルファが欲しいようなので、layeredを使えばよいかと。

コード:
    <System.Runtime.InteropServices.DllImportAttribute("gdi32.dll")> _
    Private Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
    End Function

    <Runtime.InteropServices.DllImport("gdi32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Public Shared Function SelectObject(ByVal hDC As IntPtr, ByVal hObject As IntPtr) As IntPtr
    End Function

    <System.Runtime.InteropServices.DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Public Shared Function UpdateLayeredWindow( _
        ByVal hwnd As IntPtr, _
        ByVal hdcDst As IntPtr, _
        <System.Runtime.InteropServices.In()> ByRef pptDst As System.Drawing.Point, _
        <System.Runtime.InteropServices.In()> ByRef psize As System.Drawing.Size, _
        ByVal hdcSrc As IntPtr, _
        <System.Runtime.InteropServices.In()> ByRef pptSrc As System.Drawing.Point, _
        ByRef crKey As Int32, _
        <System.Runtime.InteropServices.In()> ByRef pblend As BLENDFUNCTION, _
        ByVal dwFlags As Int32 _
       ) As Boolean
    End Function

    <System.Runtime.InteropServices.StructLayout(Runtime.InteropServices.LayoutKind.Sequential, Pack:=1)> _
     Public 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_COLORKEY As Int32 = 1
    Private Const ULW_ALPHA As Int32 = 2
    Private Const ULW_OPAQUE As Int32 = 4

    Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
        Get
            'Set WS_EX_LAYERED
            Dim cp As System.Windows.Forms.CreateParams
            cp = 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

    Public Sub SetBitmap(ByVal SrcBmp As System.Drawing.Bitmap)
        Dim g_sc As System.Drawing.Graphics
        Dim g_mem As System.Drawing.Graphics
        Dim hdc_sc As IntPtr
        Dim hdc_mem As IntPtr
        Dim hbmp As IntPtr
        Dim hbmp_old As IntPtr
        Dim blf As New BLENDFUNCTION

        g_sc = System.Drawing.Graphics.FromHwnd(IntPtr.Zero)
        g_mem = System.Drawing.Graphics.FromImage(SrcBmp)
        hdc_sc = g_sc.GetHdc()
        hdc_mem = g_mem.GetHdc()
        hbmp = SrcBmp.GetHbitmap(Drawing.Color.Black)
        hbmp_old = SelectObject(hdc_mem, hbmp)

        blf.BlendOp = AC_SRC_OVER
        blf.BlendFlags = 0
        blf.SourceConstantAlpha = 255
        blf.AlphaFormat = AC_SRC_ALPHA

        Dim r As Boolean
        r = UpdateLayeredWindow( _
            Me.Handle, _
            hdc_sc, Me.Location, New System.Drawing.Size(SrcBmp.Width, SrcBmp.Height), _
            hdc_mem, New System.Drawing.Point(0, 0), _
             0, blf, ULW_ALPHA)
        SelectObject(hdc_mem, hbmp_old)
        DeleteObject(hbmp)
        g_mem.ReleaseHdc(hdc_mem)
        g_sc.ReleaseHdc(hdc_sc)
        g_mem.Dispose()
        g_sc.Dispose()

        If Not r Then Throw New ApplicationException(System.Runtime.InteropServices.Marshal.GetLastWin32Error().ToString)
    End Sub

toto
会議室デビュー日: 2007/09/15
投稿数: 4
投稿日時: 2007-09-16 11:14
お返事ありがとうございました!
VBはわからないのですが(書いてませんでしたがC#を使っています)、UpdateLayeredWindowを使えばよいということは分かったので、なんとかなると思います。とても参考になりました。
1

スキルアップ/キャリアアップ(JOB@IT)