如何使用SharpDX绘制透明表面?

时间:2016-06-23 12:07:53

标签: c# .net vb.net directx sharpdx

(此问题是基于对 this 其他问题的进一步调查,但不是同一个问题,这是关于绘画问题的非常具体的问题。)

我试图在目标窗口上绘制一个重叠的透明表面,问题是我不知道如何将其涂成透明的,所以到目前为止我的表面是黑色的,我看不到在下面的代码中清除该表面的黑色的正确方法。

我读过有关pixelformats和alphamodes的内容,但是,似乎我不能使用AlphaMode.Straight,这可能是为了允许透明度。

我知道可以执行此操作的免费软件应用程序,其名称为 TurboHUD (在游戏客户端窗口上绘制透明表面的应用程序对象,即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

上面的代码是对 this 问题的接受答案的VB.NET改编。

1 个答案:

答案 0 :(得分:3)

非常感谢@ γηράσκωδ'αείπολλάδιδασκόμε建议我最终使用 SharpDx 来做到这一点。

下面的代码包含对外部库的一些调用,但我认为这个想法非常明确。

作为@ γηράσκωδ'αείπολλάδιδασκόμε说,使用WindowRenderTarget似乎我需要以我自己的形式使用它,我的形式必须满足这些条件:

  • 有黑色背景色。
  • 是一个无国界的形式。
  • 是最顶层的窗口(显而易见)。
  • 通过调用 DwmExtendFrameIntoClientArea 功能,必须将窗框扩展到客户区。

然后我可以调用方法WindowRenderTarget.Clear(Color.Transparent)来使表单透明。请注意,Clear()方法不适用于除我们自己的Form之外的任何其他窗口,具有上述条件,这意味着如果我们尝试直接在目标窗口上绘制透明表面而不是使用我们的表单来执行此操作,我们会产生一个不能透明的纯色表面(我真的不明白为什么不能。)

因此,在完成所有提到的基本步骤之后,现在只需要抛光一些东西,比如目标窗口顶部的源窗口重叠(产生HUD效果),处理目标窗口调整大小,你想要什么。下面的代码只是示范性的,我暂时还不能很好地处理这些事情。

以下是代码:

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