【问题标题】:Form's TransparencyKey leaves ghastly colored edging [closed]表单的 TransparencyKey 留下可怕的彩色边缘[关闭]
【发布时间】:2020-06-26 08:24:31
【问题描述】:

TransparencyKey 用于制作透明表单并将具有透明像素的 PNG 作为背景图像放置时,PNG 图像的大部分图像轮廓(尤其是用于设置也形成透明度键......在我的情况下是洋红色)。

我也尝试了几个小时也想出了一个解决方案,但找不到。

几乎起作用的是使用具有不同TransparencyKey颜色的多个表单,如果像素与其他表单之一中的颜色或位置不匹配,则逐像素匹配表单,如果匹配则被排除在外在新表单上逐像素写入。

它并不完美,但非常接近。然而,这种方法实际上需要 2.5 小时,这对于处理一个小徽标来说太长了。

我该如何解决这个问题?

【问题讨论】:

  • 谷歌“vb.net per-pixel alpha透明度”来查找命中。

标签: .net vb.net winforms transparency


【解决方案1】:

此代码是对此处代码的翻译(有少量解释):
Windows Form Transparent Background Image.
最初来自 Microsoft 示例代码库(至少在他们杀死它之前是这样)。


当窗体呈现透明时,将其 TransparencyKey 设置为与 BackGroundColor 使用的相同颜色,然后在透明上绘制半透明位图在窗体的表面,位图的抗锯齿部分不会与窗体后面的任何东西混合。
用作TransparencyKey 的颜色可能会影响渲染结果,但半透明像素(尤其是位图边缘附近的像素)将始终在不同背景上可见,因为没有混合 .

为了解决这个问题,我们可以建一个Layered Window

系统自动组合重绘分层窗口, 底层应用程序的窗口。因此,分层窗口是 渲染流畅,没有复杂窗口典型的闪烁 地区。此外,分层窗口可以是部分半透明的, 也就是说,alpha 混合。

要创建分层表单,我们可以设置 WS_EX_LAYERED 扩展样式覆盖表单的 CreateParams 属性:

Protected Overrides ReadOnly Property CreateParams As CreateParams
    Get
        Dim parms As CreateParams = MyBase.CreateParams
        parms.ExStyle = parms.ExStyle Or WS_EX_LAYERED
        Return parms
    End Get
End Property

Windows 8+:顶级窗口和子窗口支持WS_EX_LAYERED 样式。以前的 Windows 版本仅对顶级窗口支持此样式。

要绘制一个可以与背景混合的位图,我们在窗口设备上下文中选择一个位图,然后调用UpdateLayeredWindow,使用BLENDFUNCTION 结构指定渲染类型。
此结构允许定义 (BlendOp) 如何混合源位图和目标位图(实际上,唯一可能的操作是 Source Over,AC_SRC_OVER),级别应用于源位图的不透明度(SourceConstantAlpha255 = 不透明,0 = 完全透明)以及如何解释源位图和目标位图的颜色(@987654340 @)。

在这里,我们想要混合具有 Alpha 通道(每像素 Alpha)的源位图,因此它是半透明的:我们将 AC_SRC_ALPHA 指定为 AlphaFormat(参见关于如何根据源 bItmap 的颜色类型解释颜色混合的文档)。

就是这样。

要构建分层表单,请将新表单添加到项目中,更改构造函数,如下所示,添加 CreateParams 覆盖,WndProc 覆盖(如果表单可以拖动它)和 @ 987654345@ 方法调用,激活源 Bitmap(在构造函数中传递)和 Form 的 DC 的 alpha 混合。
另外,将NativeMethods 支持类添加到项目中:

► 可以像往常一样创建表单,在这种情况下,将 Bitmap 对象传递给它的构造函数:
(位图格式必须是 32 位 ARGB - 带有 alpha 通道的 PNG 即可)

Dim layeredForm As New PerPixelAlphaLayeredForm(bitmap)
layeredForm.Show()

Public Class PerPixelAlphaLayeredForm
    Public Sub New()
        Me.New(Nothing)
    End Sub

    Public Sub New(bitmap As Bitmap)
        InitializeComponent()
        Me.LayerBitmap = bitmap
    End Sub

    Private ReadOnly Property LayerBitmap As Bitmap

    Protected Overrides Sub OnLoad(e As EventArgs)
        MyBase.OnLoad(e)
        If Me.LayerBitmap IsNot Nothing Then
            Me.ClientSize = Me.LayerBitmap.Size
            Dim screenSize = Screen.FromHandle(Me.Handle).Bounds.Size
            Me.Location = New Point((screenSize.Width - Me.Width) \ 2, (screenSize.Height - Me.Height) \ 2)
            SelectBitmap(Me.LayerBitmap)
            ' Or, call the SelectBitmapFadeOut() method
            ' Task.Run(Function() SelectBitmapFadeOut(Me.LayerBitmap))
        End If
        Me.TopMost = True
    End Sub

    Private Sub SelectBitmap(bitmap As Bitmap)
        NativeMethods.SelectBitmapToLayeredWindow(Me, bitmap, 255)
    End Sub

    Private Async Function SelectBitmapFadeOut(bitmap As Bitmap) As Task
        Dim fadeProgress As Integer = 255
        For i = fadeProgress To 1 Step -1
            BeginInvoke(New MethodInvoker(Sub() NativeMethods.SelectBitmapToLayeredWindow(Me, bitmap, fadeProgress)))
            fadeProgress -= 1
            Await Task.Delay(10)
        Next
    End Function

    Protected Overrides ReadOnly Property CreateParams As CreateParams
        Get
            Dim parms As CreateParams = MyBase.CreateParams
            If Not DesignMode Then parms.ExStyle = parms.ExStyle Or NativeMethods.WS_EX_LAYERED
            Return parms
        End Get
    End Property

    Protected Overrides Sub WndProc(ByRef m As Message)
        If m.Msg = NativeMethods.WM_NCHITTEST Then
            m.Result = New IntPtr(NativeMethods.HTCAPTION)
        Else
            MyBase.WndProc(m)
        End If
    End Sub
End Class

NativeMethods 支持类:

Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices

Friend Class NativeMethods
    Public Const HTCAPTION As Integer = &H2
    Public Const WM_PAINT = &HF
    Public Const WM_NCHITTEST As Integer = &H84
    Public Const WS_EX_LAYERED As Integer = &H80000

    Public Const AC_SRC_OVER As Byte = 0
    Public Const AC_SRC_ALPHA As Byte = 1

    <Flags>
    Friend Enum ULWFlags
        ULW_COLORKEY = &H1
        ULW_ALPHA = &H2
        ULW_OPAQUE = &H4
        ULW_EX_NORESIZE = &H8
    End Enum

    <StructLayout(LayoutKind.Sequential)>
    Friend Structure POINT
        Public x As Integer
        Public y As Integer
        Public Sub New(X As Integer, Y As Integer)
            Me.x = X
            Me.y = Y
        End Sub
    End Structure

    <StructLayout(LayoutKind.Sequential)>
    Friend Structure SIZE
        Public cx As Integer
        Public cy As Integer
        Public Sub New(cX As Integer, cY As Integer)
            Me.cx = cX
            Me.cy = cY
        End Sub
    End Structure

    <StructLayout(LayoutKind.Sequential, Pack:=1)>
    Friend Structure ARGB
        Public Blue As Byte
        Public Green As Byte
        Public Red As Byte
        Public Alpha As Byte
    End Structure

    <StructLayout(LayoutKind.Sequential, Pack:=1)>
    Friend Structure BLENDFUNCTION
        Public BlendOp As Byte
        Public BlendFlags As Byte
        Public SourceConstantAlpha As Byte
        Public AlphaFormat As Byte
    End Structure

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function UpdateLayeredWindow(hWnd As IntPtr, hdcDst As IntPtr, ByRef pptDst As POINT,
        ByRef psize As SIZE, hdcSrc As IntPtr, ByRef pprSrc As POINT, crKey As Integer,
        ByRef pblend As BLENDFUNCTION, dwFlags As ULWFlags) As Boolean
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function SetLayeredWindowAttributes(hWnd As IntPtr, crKey As Integer,
        bAlpha As Byte, dwFlags As ULWFlags) As Boolean
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function ReleaseDC(hWnd As IntPtr, hDC As IntPtr) As Integer
    End Function

    <DllImport("gdi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function CreateCompatibleDC(hDC As IntPtr) As IntPtr
    End Function

    <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function GetDC(hWnd As IntPtr) As IntPtr
    End Function

    <DllImport("gdi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function DeleteDC(hdc As IntPtr) As Boolean
    End Function

    <DllImport("gdi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function SelectObject(hDC As IntPtr, hObject As IntPtr) As IntPtr
    End Function

    <DllImport("gdi32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
    Friend Shared Function DeleteObject(hObject As IntPtr) As Boolean
    End Function

    Public Shared Sub SelectBitmapToLayeredWindow(form As Form, bitmap As Bitmap, opacity As Integer)

        If bitmap.PixelFormat <> PixelFormat.Format32bppArgb Then
            Throw New ApplicationException("The bitmap must be 32bpp with alpha-channel.")
        End If

        Dim screenDc As IntPtr = GetDC(IntPtr.Zero)
        Dim sourceDc As IntPtr = CreateCompatibleDC(screenDc)
        Dim hBitmap As IntPtr = IntPtr.Zero
        Dim hOldBitmap As IntPtr = IntPtr.Zero

        Try
            ' Get handle to the New bitmap and select it into the current device context.
            hBitmap = bitmap.GetHbitmap(Color.FromArgb(0))
            hOldBitmap = SelectObject(sourceDc, hBitmap)

            Dim windowLocation As New POINT(form.Left, form.Top)
            Dim windowSize As New SIZE(bitmap.Width, bitmap.Height)
            Dim sourceLocation As New POINT(0, 0)
            Dim blend As New BLENDFUNCTION() With {
                .BlendOp = AC_SRC_OVER,
                .BlendFlags = 0,
                .SourceConstantAlpha = CType(opacity, Byte),
                .AlphaFormat = AC_SRC_ALPHA
            }

            ' Update the window.
            ' Handle =>         Handle to the layered window
            ' screenDc =>       Handle to the screen DC
            ' windowLocation => Screen position of the layered window
            ' windowSize =>     SIZE of the layered window
            ' sourceDc =>       Handle to the layered window surface DC
            ' sourceLocation => Location of the layer in the DC
            ' 0 =>              Color key of the layered window
            ' blend =>          Transparency of the layered window
            ' ULW_ALPHA =>      Use blend as the blend function
            UpdateLayeredWindow(form.Handle, screenDc, windowLocation, windowSize,
                                sourceDc, sourceLocation, 0, blend, ULWFlags.ULW_ALPHA)
        Finally
            ' Release device context.
            ReleaseDC(IntPtr.Zero, screenDc)
            If hBitmap <> IntPtr.Zero Then
                SelectObject(sourceDc, hOldBitmap)
                DeleteObject(hBitmap)
            End If
            DeleteDC(sourceDc)
        End Try
    End Sub
End Class

您可以从 Google 云端硬盘下载 Sample Project
使用 .Net Framework 4.7.2 构建 - 任何其他框架 4.5.2+ 都可以

【讨论】:

  • 感谢吉姆的回复。我不是高级用户。我无法理解代码的所有部分的去向。为了表明我已经做出了努力,我有 1) 开始一个新项目,然后我添加了您在表单代码中提供的受保护的覆盖代码,就在“公共课程表单 1”的下方。 2:创建了一个新表单 PerPixelAlphaLayeredForm,其中包含您的第二个代码块和 3:创建一个类 NativeMethods,我添加了您的最后一个代码块。我显然做错了我不知道在哪里...对不起
  • PerPixelAlphaLayeredForm 是一个表单名称。您应该刚刚将您的Form1 重命名为PerPixelAlphaLayeredForm,将代码粘贴到其中的第一个块(从Public Sub New() 开始),仅此而已。 NativeMethods 类通常是一个独立的类对象:您应该向您的项目添加一个新类,将其命名为 NativeMethods,然后将其粘贴到此处覆盖现有(2 行)代码的代码。但是你也可以将它粘贴到你的Form中(你的Form1,重命名或不重命名为PerPixelAlphaLayeredForm,都是一样的)。
  • 请注意,现在构建的分层表单应该是从其他代码中实例化的,因为如您所见,位图是在其构造函数中分配的,LayerBitmap As Bitmap 属性是 @ 987654361@。这是因为它应该用作Splash Windows,在需要的时间保留在那里,然后由相同的调用代码处理。这不是绝对必要的,但我建议您像这样使用它,因为分层表单并不完全可用。所以也许,为了测试,从另一个表单中的Button.Click 事件处理程序创建它,直到你掌握它。
  • 好的,我已经掌握了您的第二个回复,现在没有错误出现。如果我理解正确,一个名为 layered 的表单是由像按钮这样的触发器创建的。该触发器背后的代码是 Dim layeredForm As New PerPixelAlphaLayeredForm(bitmap) layeredForm.Show() 现在我什么也没得到?
  • 您的项目中现在有 2 个表单吗?起始表单和另一个名为 PerPixelAlphaLayeredForm?创建时是否将半透明 PNG 位图传递给 PerPixelAlphaLayeredForm Form 构造函数?看,告诉我你正在使用哪个框架版本(如果你有 4.7.2 或 4.8 更好),我会在 Google Drive 或其他任何地方发布一个示例项目。
猜你喜欢
  • 1970-01-01
  • 2021-09-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-08
  • 2011-11-18
  • 1970-01-01
  • 2016-05-08
  • 2019-03-25
相关资源
最近更新 更多