Как нарисовать прозрачную поверхность с помощью 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