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

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

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

Я читал о пиксельных форматах и ​​альфа-режимах, однако, кажется, я не могу использовать 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

Приведенный выше код представляет собой адаптацию 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
Другие вопросы по тегам