如何使用 SharpDX 绘制透明表面?
How to draw a transparent surface using SharpDX?
(这个问题是基于对 this 其他问题的进一步调查,但不是同一个问题,这是关于绘画问题的非常具体的问题。)
我正在尝试绘制一个重叠在目标上的透明表面 window,问题是我不知道如何将其涂成透明,所以现在我的表面是黑色的,我在下面的代码中看不到清除该表面黑色的正确方法。
我读过有关像素格式和 alphamodes 的内容,但是,我似乎无法使用据说是为了允许透明度的 AlphaMode.Straight
。
我知道一个可以做到这一点的免费软件应用程序,它的名字是 TurboHUD(一个在 window 绘制对象的游戏客户端,即 HUD)。老实说,也许很荒谬:我从两年多前就开始尝试实现这一点,但我仍然不知道如何通过设置透明度来开始这样做,我需要开始在透明表面上绘制对象。
我做错了什么?此示例代码是用 VB.NET 编写的,但我也接受 C# 中的解决方案。
Imports SharpDX
Imports SharpDX.Direct2D1
Imports SharpDX.Direct3D
Imports SharpDX.DXGI
Imports SharpDX.Mathematics.Interop
Imports SharpDX.Windows
Public NotInheritable Class Form1 : Inherits Form
Private factory As New Direct2D1.Factory(Direct2D1.FactoryType.SingleThreaded)
Private render As WindowRenderTarget
Private renderProps As HwndRenderTargetProperties
Private renderThread As Thread = Nothing
Private Sub Form1_Load() Handles MyBase.Shown
Dim hwnd As IntPtr = Process.GetProcessesByName("notepad").Single().MainWindowHandle
Me.renderProps = New HwndRenderTargetProperties()
Me.renderProps.Hwnd = hwnd
Me.renderProps.PixelSize = New Size2(1920, 1080)
Me.renderProps.PresentOptions = PresentOptions.None
Me.render = New WindowRenderTarget(Me.factory, New RenderTargetProperties(New PixelFormat(Format.B8G8R8A8_UNorm, Direct2D1.AlphaMode.Premultiplied)), Me.renderProps)
Me.renderThread = New Thread(New ParameterizedThreadStart(AddressOf Me.DoRender))
Me.renderThread.Priority = ThreadPriority.Normal
Me.renderThread.IsBackground = True
Me.renderThread.Start()
End Sub
Private Sub DoRender(ByVal sender As Object)
While True
Me.render.BeginDraw()
' Me.render.Clear(New RawColor4(0, 0, 0, 0))
Me.render.Clear(SharpDX.Color.Transparent)
Me.render.Flush()
Me.render.EndDraw()
End While
End Sub
End Class
上面的代码是 VB.NET 对 this 问题的已接受答案的改编。
非常感谢@γηράσκωδ' αεί πολλì διδασκόμε 的建议,我最终使用 SharpDx.
下面的代码包含一些对外部库的调用,但我认为思路会很清晰。
正如@γηράσκω δ' αεί πολλì διδασκόμε 所说,要使用WindowRenderTarget
看来我需要在我自己的形式中使用它,我的形式必须满足这些条件:
- 背景颜色为黑色。
- 成为一个无边界的表格。
- 成为最顶层的window(显而易见)。
- 必须通过调用DwmExtendFrameIntoClientArea函数将window框架扩展到客户区。
然后我可以调用WindowRenderTarget.Clear(Color.Transparent)
方法来使窗体透明。请注意,Clear()
方法不适用于任何其他 window 除了我们自己的具有上述条件的 Form,这意味着如果我们尝试直接在目标上绘制透明表面 window而不是使用我们的表单来做到这一点,我们将产生一个不能透明的纯色表面(我真的不明白为什么不能。)
所以在完成所有提到的基本步骤之后,现在只需要润色一些东西,比如将源-window 重叠在目标-window 的顶部(到产生 HUD 效果),处理目标 window 调整大小,以及您想要的。下面的代码只是示范性的,我暂时还不能很好地处理这些事情。
代码如下:
Imports D2D1 = SharpDX.Direct2D1
Imports D3D = SharpDX.Direct3D
Imports DXGI = SharpDX.DXGI
Imports DxColor = SharpDX.Color
Imports DxPoint = SharpDX.Point
Imports DxRectangle = SharpDX.Rectangle
Imports DxSize = SharpDX.Size2
Imports Device = SharpDX.Direct3D11.Device
Imports MapFlags = SharpDX.Direct3D11.MapFlags
Imports Elektro.Imaging.Tools
Imports Elektro.Interop.Win32
Imports Elektro.Interop.Win32.Enums
Imports Elektro.Interop.Win32.Types
Public NotInheritable Class Form1 : Inherits Form
<DllImport("dwmapi.dll")>
Private Shared Function DwmExtendFrameIntoClientArea(ByVal hwnd As IntPtr, ByRef margins As Margins) As Integer
End Function
Private factory As New D2D1.Factory(D2D1.FactoryType.SingleThreaded)
Private render As D2D1.WindowRenderTarget
Private renderProps As D2D1.HwndRenderTargetProperties
Private renderThread As Thread = Nothing
Private srcHwnd As IntPtr
Private dstHwnd As IntPtr
Private Sub Form1_Load() Handles MyBase.Shown
' Window handles of source and target window.
Me.srcHwnd = Me.Handle
Me.dstHwnd = Process.GetProcessesByName("notepad").Single().MainWindowHandle
' Form settings.
Me.BackColor = Color.Black
Me.FormBorderStyle = FormBorderStyle.None
Me.TopMost = True
' DWM stuff for later to be able make transparent the source window.
Dim rc As NativeRectangle ' a win32 RECT
NativeMethods.GetClientRect(srcHwnd, rc)
Dim margins As Margins
margins.TopHeight = rc.Width
margins.BottomHeight = rc.Height
DwmExtendFrameIntoClientArea(srcHwnd, margins)
' ------------------------------------------------
Me.renderProps = New D2D1.HwndRenderTargetProperties()
Me.renderProps.Hwnd = srcHwnd
Me.renderProps.PixelSize = New DxSize(rc.Width, rc.Height)
Me.renderProps.PresentOptions = D2D1.PresentOptions.None
Me.render = New D2D1.WindowRenderTarget(Me.factory, New D2D1.RenderTargetProperties(New D2D1.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D1.AlphaMode.Premultiplied)), Me.renderProps)
Me.renderThread = New Thread(New ParameterizedThreadStart(AddressOf Me.DoRender))
Me.renderThread.Priority = ThreadPriority.Normal
Me.renderThread.IsBackground = True
Me.renderThread.Start()
End Sub
Private Sub DoRender(ByVal sender As Object)
While True
Me.OverlapToWindow(Me.srcHwnd, Me.dstHwnd)
Me.render.BeginDraw()
Me.render.Clear(DxColor.Transparent)
Me.render.Flush()
Me.render.EndDraw()
End While
End Sub
Private Sub OverlapToWindow(ByVal srcHwnd As IntPtr, ByVal dstHwnd As IntPtr)
' Gets the (non-client) Rectangle of the windows, taking into account a borderless window of Windows 10.
Dim srcRect As Rectangle = ImageUtil.GetRealWindowRect(srcHwnd)
Dim dstRect As Rectangle = ImageUtil.GetRealWindowRect(dstHwnd)
NativeMethods.SetWindowPos(srcHwnd, dstHwnd,
dstRect.X, dstRect.Y, dstRect.Top, dstRect.Left,
SetWindowPosFlags.IgnoreZOrder Or SetWindowPosFlags.IgnoreResize)
End Sub
End Class
(这个问题是基于对 this 其他问题的进一步调查,但不是同一个问题,这是关于绘画问题的非常具体的问题。)
我正在尝试绘制一个重叠在目标上的透明表面 window,问题是我不知道如何将其涂成透明,所以现在我的表面是黑色的,我在下面的代码中看不到清除该表面黑色的正确方法。
我读过有关像素格式和 alphamodes 的内容,但是,我似乎无法使用据说是为了允许透明度的 AlphaMode.Straight
。
我知道一个可以做到这一点的免费软件应用程序,它的名字是 TurboHUD(一个在 window 绘制对象的游戏客户端,即 HUD)。老实说,也许很荒谬:我从两年多前就开始尝试实现这一点,但我仍然不知道如何通过设置透明度来开始这样做,我需要开始在透明表面上绘制对象。
我做错了什么?此示例代码是用 VB.NET 编写的,但我也接受 C# 中的解决方案。
Imports SharpDX
Imports SharpDX.Direct2D1
Imports SharpDX.Direct3D
Imports SharpDX.DXGI
Imports SharpDX.Mathematics.Interop
Imports SharpDX.Windows
Public NotInheritable Class Form1 : Inherits Form
Private factory As New Direct2D1.Factory(Direct2D1.FactoryType.SingleThreaded)
Private render As WindowRenderTarget
Private renderProps As HwndRenderTargetProperties
Private renderThread As Thread = Nothing
Private Sub Form1_Load() Handles MyBase.Shown
Dim hwnd As IntPtr = Process.GetProcessesByName("notepad").Single().MainWindowHandle
Me.renderProps = New HwndRenderTargetProperties()
Me.renderProps.Hwnd = hwnd
Me.renderProps.PixelSize = New Size2(1920, 1080)
Me.renderProps.PresentOptions = PresentOptions.None
Me.render = New WindowRenderTarget(Me.factory, New RenderTargetProperties(New PixelFormat(Format.B8G8R8A8_UNorm, Direct2D1.AlphaMode.Premultiplied)), Me.renderProps)
Me.renderThread = New Thread(New ParameterizedThreadStart(AddressOf Me.DoRender))
Me.renderThread.Priority = ThreadPriority.Normal
Me.renderThread.IsBackground = True
Me.renderThread.Start()
End Sub
Private Sub DoRender(ByVal sender As Object)
While True
Me.render.BeginDraw()
' Me.render.Clear(New RawColor4(0, 0, 0, 0))
Me.render.Clear(SharpDX.Color.Transparent)
Me.render.Flush()
Me.render.EndDraw()
End While
End Sub
End Class
上面的代码是 VB.NET 对 this 问题的已接受答案的改编。
非常感谢@γηράσκωδ' αεί πολλì διδασκόμε 的建议,我最终使用 SharpDx.
下面的代码包含一些对外部库的调用,但我认为思路会很清晰。
正如@γηράσκω δ' αεί πολλì διδασκόμε 所说,要使用WindowRenderTarget
看来我需要在我自己的形式中使用它,我的形式必须满足这些条件:
- 背景颜色为黑色。
- 成为一个无边界的表格。
- 成为最顶层的window(显而易见)。
- 必须通过调用DwmExtendFrameIntoClientArea函数将window框架扩展到客户区。
然后我可以调用WindowRenderTarget.Clear(Color.Transparent)
方法来使窗体透明。请注意,Clear()
方法不适用于任何其他 window 除了我们自己的具有上述条件的 Form,这意味着如果我们尝试直接在目标上绘制透明表面 window而不是使用我们的表单来做到这一点,我们将产生一个不能透明的纯色表面(我真的不明白为什么不能。)
所以在完成所有提到的基本步骤之后,现在只需要润色一些东西,比如将源-window 重叠在目标-window 的顶部(到产生 HUD 效果),处理目标 window 调整大小,以及您想要的。下面的代码只是示范性的,我暂时还不能很好地处理这些事情。
代码如下:
Imports D2D1 = SharpDX.Direct2D1
Imports D3D = SharpDX.Direct3D
Imports DXGI = SharpDX.DXGI
Imports DxColor = SharpDX.Color
Imports DxPoint = SharpDX.Point
Imports DxRectangle = SharpDX.Rectangle
Imports DxSize = SharpDX.Size2
Imports Device = SharpDX.Direct3D11.Device
Imports MapFlags = SharpDX.Direct3D11.MapFlags
Imports Elektro.Imaging.Tools
Imports Elektro.Interop.Win32
Imports Elektro.Interop.Win32.Enums
Imports Elektro.Interop.Win32.Types
Public NotInheritable Class Form1 : Inherits Form
<DllImport("dwmapi.dll")>
Private Shared Function DwmExtendFrameIntoClientArea(ByVal hwnd As IntPtr, ByRef margins As Margins) As Integer
End Function
Private factory As New D2D1.Factory(D2D1.FactoryType.SingleThreaded)
Private render As D2D1.WindowRenderTarget
Private renderProps As D2D1.HwndRenderTargetProperties
Private renderThread As Thread = Nothing
Private srcHwnd As IntPtr
Private dstHwnd As IntPtr
Private Sub Form1_Load() Handles MyBase.Shown
' Window handles of source and target window.
Me.srcHwnd = Me.Handle
Me.dstHwnd = Process.GetProcessesByName("notepad").Single().MainWindowHandle
' Form settings.
Me.BackColor = Color.Black
Me.FormBorderStyle = FormBorderStyle.None
Me.TopMost = True
' DWM stuff for later to be able make transparent the source window.
Dim rc As NativeRectangle ' a win32 RECT
NativeMethods.GetClientRect(srcHwnd, rc)
Dim margins As Margins
margins.TopHeight = rc.Width
margins.BottomHeight = rc.Height
DwmExtendFrameIntoClientArea(srcHwnd, margins)
' ------------------------------------------------
Me.renderProps = New D2D1.HwndRenderTargetProperties()
Me.renderProps.Hwnd = srcHwnd
Me.renderProps.PixelSize = New DxSize(rc.Width, rc.Height)
Me.renderProps.PresentOptions = D2D1.PresentOptions.None
Me.render = New D2D1.WindowRenderTarget(Me.factory, New D2D1.RenderTargetProperties(New D2D1.PixelFormat(DXGI.Format.B8G8R8A8_UNorm, D2D1.AlphaMode.Premultiplied)), Me.renderProps)
Me.renderThread = New Thread(New ParameterizedThreadStart(AddressOf Me.DoRender))
Me.renderThread.Priority = ThreadPriority.Normal
Me.renderThread.IsBackground = True
Me.renderThread.Start()
End Sub
Private Sub DoRender(ByVal sender As Object)
While True
Me.OverlapToWindow(Me.srcHwnd, Me.dstHwnd)
Me.render.BeginDraw()
Me.render.Clear(DxColor.Transparent)
Me.render.Flush()
Me.render.EndDraw()
End While
End Sub
Private Sub OverlapToWindow(ByVal srcHwnd As IntPtr, ByVal dstHwnd As IntPtr)
' Gets the (non-client) Rectangle of the windows, taking into account a borderless window of Windows 10.
Dim srcRect As Rectangle = ImageUtil.GetRealWindowRect(srcHwnd)
Dim dstRect As Rectangle = ImageUtil.GetRealWindowRect(dstHwnd)
NativeMethods.SetWindowPos(srcHwnd, dstHwnd,
dstRect.X, dstRect.Y, dstRect.Top, dstRect.Left,
SetWindowPosFlags.IgnoreZOrder Or SetWindowPosFlags.IgnoreResize)
End Sub
End Class