Как нарисовать прозрачную поверхность с помощью SharpDX?

(Этот вопрос основан на дальнейших исследованиях этого другого вопроса, но это не тот же вопрос, это очень конкретный вопрос о проблемах с живописью.)

Я пытаюсь нарисовать прозрачную поверхность, наложенную на целевое окно, проблема в том, что я не знаю, как нарисовать ее прозрачно, поэтому к моменту, когда моя поверхность черная, и я не вижу правильного пути к очистите черный цвет этой поверхности в коде ниже.

Я читал о пиксельных форматах и ​​альфа-методах, однако, кажется, я не могу использовать AlphaMode.Straight, который якобы предназначен для прозрачности.

Я знаю бесплатное приложение, которое может это сделать, его имя TurboHUD ( приложение, которое рисует прозрачную поверхность в окне игрового клиента для рисования объектов, то есть HUD). Если честно и, может быть, смешно: я пытаюсь это сделать более двух лет назад, я до сих пор не знаю, как начать делать это, выполняя прозрачность, необходимую мне для рисования объектов на прозрачной поверхности.

Что я делаю неправильно? Этот пример кода написан на VB.NET, но я тоже принимаю решение на С#.

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 принятого ответа на вопрос .

Ответ 1

Большое спасибо @ γηράσκω δ 'αεί πολλά διδασκόμε предложения, которые я, наконец, получил, чтобы сделать это, используя SharpDx.

В приведенном ниже коде содержатся некоторые вызовы во внешнюю библиотеку, однако я думаю, что идея будет очень понятной.

Как сказал @ γηράσκω δ 'αεί πολλά διδασκόμε, чтобы использовать WindowRenderTarget, мне нужно использовать его в своей собственной форме, и моя форма должна удовлетворять следующим условиям:

  • Есть черный цвет фона.
  • Будьте без полей.
  • Быть самым верхним окном (очевидно).
  • Рамка окна должна быть расширена в клиентскую область, вызвав DwmExtendFrameIntoClientArea.

Затем я могу вызвать метод WindowRenderTarget.Clear(Color.Transparent), чтобы сделать форму прозрачной. Обратите внимание, что метод Clear() не будет работать ни для какого другого окна, кроме нашей собственной Формы с указанными выше условиями, это означает, что если мы попытаемся нарисовать прозрачную поверхность непосредственно в целевом окне вместо использования нашей формы для этого, мы будет создавать сплошную поверхность, которая не может быть прозрачной (я действительно не понимаю, почему не могу).

Таким образом, после того, как все основные этапы будут выполнены, теперь будет просто вопрос полировки, например перекрытие исходного окна в верхней части целевого окна (для создания эффекта 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