From 1fc0301551e98b4fc668d941a7291240cd12f1ef Mon Sep 17 00:00:00 2001 From: huiyadanli Date: Sat, 7 Oct 2023 23:26:56 +0800 Subject: [PATCH] refactor Fischless.WindowCapture & add DwmSharedSurface capture --- BetterGenshinImpact.sln | 4 +- .../BetterGenshinImpact.csproj | 2 +- BetterGenshinImpact/Core/Config/AllConfig.cs | 10 +- .../GameTask/TaskDispatcher.cs | 12 +- .../View/CaptureTestWindow.xaml.cs | 10 +- .../ViewModel/MainWindowViewModel.cs | 15 +-- .../ViewModel/Pages/HomePageViewModel.cs | 30 ++--- Fischless.GameCapture/BitBlt/BitBltCapture.cs | 56 +++++++++ .../CaptureModeExtensions.cs | 2 +- .../CaptureModes.cs | 3 +- .../DwmSharedSurface/Helpers/NativeMethods.cs | 23 ++++ .../DwmSharedSurface/SharedSurfaceCapture.cs | 115 ++++++++++++++++++ .../Fischless.GameCapture.csproj | 0 .../GameCaptureFactory.cs | 7 +- .../Graphics/GraphicsCapture.cs | 31 ++--- .../Graphics/Helpers/CaptureHelper.cs | 2 +- .../Graphics/Helpers/Direct3D11Helper.cs | 4 +- .../Graphics/Helpers/Texture2DExtensions.cs | 12 +- .../Graphics/Helpers/WinRT.cs | 2 +- Fischless.GameCapture/IGameCapture.cs | 12 ++ .../BitBlt/BitBltCapture.cs | 113 ----------------- Fischless.WindowCapture/IWindowCapture.cs | 14 --- 22 files changed, 268 insertions(+), 211 deletions(-) create mode 100644 Fischless.GameCapture/BitBlt/BitBltCapture.cs rename {Fischless.WindowCapture => Fischless.GameCapture}/CaptureModeExtensions.cs (83%) rename {Fischless.WindowCapture => Fischless.GameCapture}/CaptureModes.cs (53%) create mode 100644 Fischless.GameCapture/DwmSharedSurface/Helpers/NativeMethods.cs create mode 100644 Fischless.GameCapture/DwmSharedSurface/SharedSurfaceCapture.cs rename Fischless.WindowCapture/Fischless.WindowCapture.csproj => Fischless.GameCapture/Fischless.GameCapture.csproj (100%) rename Fischless.WindowCapture/WindowCaptureFactory.cs => Fischless.GameCapture/GameCaptureFactory.cs (64%) rename {Fischless.WindowCapture => Fischless.GameCapture}/Graphics/GraphicsCapture.cs (82%) rename {Fischless.WindowCapture => Fischless.GameCapture}/Graphics/Helpers/CaptureHelper.cs (96%) rename {Fischless.WindowCapture => Fischless.GameCapture}/Graphics/Helpers/Direct3D11Helper.cs (97%) rename {Fischless.WindowCapture => Fischless.GameCapture}/Graphics/Helpers/Texture2DExtensions.cs (81%) rename {Fischless.WindowCapture => Fischless.GameCapture}/Graphics/Helpers/WinRT.cs (98%) create mode 100644 Fischless.GameCapture/IGameCapture.cs delete mode 100644 Fischless.WindowCapture/BitBlt/BitBltCapture.cs delete mode 100644 Fischless.WindowCapture/IWindowCapture.cs diff --git a/BetterGenshinImpact.sln b/BetterGenshinImpact.sln index bff2c958..ea739f1a 100644 --- a/BetterGenshinImpact.sln +++ b/BetterGenshinImpact.sln @@ -9,9 +9,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vision.WindowCapture", "Vis EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BetterGenshinImpact.Win32", "BetterGenshinImpact.Win32\BetterGenshinImpact.Win32.csproj", "{75F4541B-9624-4AFB-BAEA-3EAFD3300EE1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.WindowCapture", "Fischless.WindowCapture\Fischless.WindowCapture.csproj", "{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fischless.GameCapture", "Fischless.GameCapture\Fischless.GameCapture.csproj", "{6B0A3D96-D88D-48DD-8112-4CD5BA5D27CE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vision.WindowCapture.Test", "Vision.WindowCapture.Test\Vision.WindowCapture.Test.csproj", "{D35CB953-C666-4E57-9A9A-3AAE5BF78402}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Vision.WindowCapture.Test", "Vision.WindowCapture.Test\Vision.WindowCapture.Test.csproj", "{D35CB953-C666-4E57-9A9A-3AAE5BF78402}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj index cbd430f2..09640c5f 100644 --- a/BetterGenshinImpact/BetterGenshinImpact.csproj +++ b/BetterGenshinImpact/BetterGenshinImpact.csproj @@ -38,7 +38,7 @@ - + diff --git a/BetterGenshinImpact/Core/Config/AllConfig.cs b/BetterGenshinImpact/Core/Config/AllConfig.cs index 4c74aaa8..7b34ee78 100644 --- a/BetterGenshinImpact/Core/Config/AllConfig.cs +++ b/BetterGenshinImpact/Core/Config/AllConfig.cs @@ -1,10 +1,10 @@ -using BetterGenshinImpact.GameTask.AutoFishing; -using BetterGenshinImpact.GameTask.AutoSkip; -using System; -using BetterGenshinImpact.GameTask; +using BetterGenshinImpact.GameTask; +using BetterGenshinImpact.GameTask.AutoFishing; using BetterGenshinImpact.GameTask.AutoPick; +using BetterGenshinImpact.GameTask.AutoSkip; using CommunityToolkit.Mvvm.ComponentModel; -using Fischless.WindowCapture; +using Fischless.GameCapture; +using System; using System.Text.Json.Serialization; namespace BetterGenshinImpact.Core.Config diff --git a/BetterGenshinImpact/GameTask/TaskDispatcher.cs b/BetterGenshinImpact/GameTask/TaskDispatcher.cs index 3dfc738a..bf039568 100644 --- a/BetterGenshinImpact/GameTask/TaskDispatcher.cs +++ b/BetterGenshinImpact/GameTask/TaskDispatcher.cs @@ -1,16 +1,12 @@ -using Fischless.WindowCapture; +using BetterGenshinImpact.View; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Diagnostics; -using System.Drawing; using System.Linq; -using System.Text.RegularExpressions; using System.Threading; -using System.Windows; -using BetterGenshinImpact.View; +using Fischless.GameCapture; using Vanara.PInvoke; -using System.Windows.Threading; namespace BetterGenshinImpact.GameTask { @@ -21,7 +17,7 @@ namespace BetterGenshinImpact.GameTask private readonly System.Timers.Timer _timer = new(); private List? _triggers; - private IWindowCapture? _capture; + private IGameCapture? _capture; private static readonly object _locker = new(); private int _frameIndex = 0; @@ -49,7 +45,7 @@ namespace BetterGenshinImpact.GameTask _triggers = GameTaskManager.LoadTriggers(); // 初始化截图器 - _capture = WindowCaptureFactory.Create(mode); + _capture = GameCaptureFactory.Create(mode); //_capture.IsClientEnabled = true; _capture.Start(hWnd); diff --git a/BetterGenshinImpact/View/CaptureTestWindow.xaml.cs b/BetterGenshinImpact/View/CaptureTestWindow.xaml.cs index 7c0a545d..0e02f8a9 100644 --- a/BetterGenshinImpact/View/CaptureTestWindow.xaml.cs +++ b/BetterGenshinImpact/View/CaptureTestWindow.xaml.cs @@ -1,9 +1,9 @@ -using System; +using BetterGenshinImpact.Helpers.Extensions; +using System; using System.Diagnostics; using System.Windows; using System.Windows.Media; -using BetterGenshinImpact.Helpers.Extensions; -using Fischless.WindowCapture; +using Fischless.GameCapture; namespace BetterGenshinImpact.View { @@ -12,7 +12,7 @@ namespace BetterGenshinImpact.View /// public partial class CaptureTestWindow : Window { - private IWindowCapture? _capture; + private IGameCapture? _capture; private long _captureTime; @@ -45,7 +45,7 @@ namespace BetterGenshinImpact.View } - _capture = WindowCaptureFactory.Create(captureMode); + _capture = GameCaptureFactory.Create(captureMode); //_capture.IsClientEnabled = true; _capture.Start(hWnd); diff --git a/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs b/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs index 7bac2ee6..29696719 100644 --- a/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs +++ b/BetterGenshinImpact/ViewModel/MainWindowViewModel.cs @@ -1,23 +1,12 @@ -using BetterGenshinImpact.GameTask; -using BetterGenshinImpact.Helpers; -using BetterGenshinImpact.View; +using BetterGenshinImpact.Service.Interface; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; -using Fischless.WindowCapture; +using CommunityToolkit.Mvvm.Messaging.Messages; using Microsoft.Extensions.Logging; -using System; using System.Diagnostics; using System.Windows; -using BetterGenshinImpact.Core; -using CommunityToolkit.Mvvm.Messaging.Messages; -using Vanara.PInvoke; -using System.Collections.ObjectModel; -using BetterGenshinImpact.View.Pages; using Wpf.Ui; -using Wpf.Ui.Controls; -using BetterGenshinImpact.Service.Interface; -using BetterGenshinImpact.Service; namespace BetterGenshinImpact.ViewModel { diff --git a/BetterGenshinImpact/ViewModel/Pages/HomePageViewModel.cs b/BetterGenshinImpact/ViewModel/Pages/HomePageViewModel.cs index ec0920f3..c572d57d 100644 --- a/BetterGenshinImpact/ViewModel/Pages/HomePageViewModel.cs +++ b/BetterGenshinImpact/ViewModel/Pages/HomePageViewModel.cs @@ -1,34 +1,28 @@ using BetterGenshinImpact.Core; +using BetterGenshinImpact.Core.Config; +using BetterGenshinImpact.Core.Recognition.ONNX.SVTR; using BetterGenshinImpact.GameTask; +using BetterGenshinImpact.Helpers; +using BetterGenshinImpact.Service.Interface; using BetterGenshinImpact.View; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Fischless.WindowCapture; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using CommunityToolkit.Mvvm.Messaging.Messages; using CommunityToolkit.Mvvm.Messaging; -using Wpf.Ui.Controls; -using BetterGenshinImpact.Service.Interface; -using BetterGenshinImpact.Core.Config; -using BetterGenshinImpact.Core.Recognition.ONNX.SVTR; +using CommunityToolkit.Mvvm.Messaging.Messages; +using Microsoft.Extensions.Logging; using OpenCvSharp; +using System; +using System.Diagnostics; +using System.Windows; using System.Windows.Interop; -using BetterGenshinImpact.Helpers; +using Fischless.GameCapture; +using Wpf.Ui.Controls; namespace BetterGenshinImpact.ViewModel.Pages; public partial class HomePageViewModel : ObservableObject, INavigationAware { - [ObservableProperty] private string[] _modeNames = WindowCaptureFactory.ModeNames(); + [ObservableProperty] private string[] _modeNames = GameCaptureFactory.ModeNames(); [ObservableProperty] private string? _selectedMode = CaptureModes.BitBlt.ToString(); diff --git a/Fischless.GameCapture/BitBlt/BitBltCapture.cs b/Fischless.GameCapture/BitBlt/BitBltCapture.cs new file mode 100644 index 00000000..7c8b5943 --- /dev/null +++ b/Fischless.GameCapture/BitBlt/BitBltCapture.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using Vanara.PInvoke; + +namespace Fischless.GameCapture.BitBlt; + +public class BitBltCapture : IGameCapture +{ + private nint _hWnd; + public bool IsCapturing { get; private set; } + + public void Dispose() => Stop(); + + public void Start(nint hWnd) + { + _hWnd = hWnd; + IsCapturing = true; + } + + public Bitmap? Capture() + { + if (_hWnd == IntPtr.Zero) + { + return null; + } + + try + { + User32.GetClientRect(_hWnd, out var windowRect); + int x = default, y = default; + var width = windowRect.right - windowRect.left; + var height = windowRect.bottom - windowRect.top; + + Bitmap bitmap = new(width, height); + using System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap); + var hdcDest = g.GetHdc(); + Gdi32.SafeHDC hdcSrc = User32.GetDC(_hWnd == IntPtr.Zero ? User32.GetDesktopWindow() : _hWnd); + Gdi32.StretchBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, width, height, Gdi32.RasterOperationMode.SRCCOPY); + g.ReleaseHdc(); + Gdi32.DeleteDC(hdcDest); + Gdi32.DeleteDC(hdcSrc); + return bitmap; + } + catch (Exception e) + { + Debug.WriteLine(e); + } + + return null!; + } + + public void Stop() + { + _hWnd = IntPtr.Zero; + IsCapturing = false; + } +} \ No newline at end of file diff --git a/Fischless.WindowCapture/CaptureModeExtensions.cs b/Fischless.GameCapture/CaptureModeExtensions.cs similarity index 83% rename from Fischless.WindowCapture/CaptureModeExtensions.cs rename to Fischless.GameCapture/CaptureModeExtensions.cs index d54c60c2..b2140dda 100644 --- a/Fischless.WindowCapture/CaptureModeExtensions.cs +++ b/Fischless.GameCapture/CaptureModeExtensions.cs @@ -1,4 +1,4 @@ -namespace Fischless.WindowCapture; +namespace Fischless.GameCapture; public static class CaptureModeExtensions { diff --git a/Fischless.WindowCapture/CaptureModes.cs b/Fischless.GameCapture/CaptureModes.cs similarity index 53% rename from Fischless.WindowCapture/CaptureModes.cs rename to Fischless.GameCapture/CaptureModes.cs index bf0e1f9a..c6c6834b 100644 --- a/Fischless.WindowCapture/CaptureModes.cs +++ b/Fischless.GameCapture/CaptureModes.cs @@ -1,7 +1,8 @@ -namespace Fischless.WindowCapture; +namespace Fischless.GameCapture; public enum CaptureModes { BitBlt, WindowsGraphicsCapture, + DwmGetDxSharedSurface } diff --git a/Fischless.GameCapture/DwmSharedSurface/Helpers/NativeMethods.cs b/Fischless.GameCapture/DwmSharedSurface/Helpers/NativeMethods.cs new file mode 100644 index 00000000..c0fe0a89 --- /dev/null +++ b/Fischless.GameCapture/DwmSharedSurface/Helpers/NativeMethods.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Vanara.PInvoke; + +namespace Fischless.GameCapture.DwmSharedSurface.Helpers; + +internal class NativeMethods +{ + + public delegate bool DwmGetDxSharedSurfaceDelegate(IntPtr hWnd, out IntPtr phSurface, out long pAdapterLuid, out long pFmtWindow, out long pPresentFlags, out long pWin32KUpdateId); + + public static DwmGetDxSharedSurfaceDelegate DwmGetDxSharedSurface; + + static NativeMethods() + { + var ptr = Kernel32.GetProcAddress(Kernel32.GetModuleHandle("user32"), "DwmGetDxSharedSurface"); + DwmGetDxSharedSurface = Marshal.GetDelegateForFunctionPointer(ptr); + } +} \ No newline at end of file diff --git a/Fischless.GameCapture/DwmSharedSurface/SharedSurfaceCapture.cs b/Fischless.GameCapture/DwmSharedSurface/SharedSurfaceCapture.cs new file mode 100644 index 00000000..dfdcbaac --- /dev/null +++ b/Fischless.GameCapture/DwmSharedSurface/SharedSurfaceCapture.cs @@ -0,0 +1,115 @@ +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; +using System.Windows.Controls; +using System.Windows.Forms; +using Fischless.GameCapture.DwmSharedSurface.Helpers; +using Fischless.GameCapture.Graphics.Helpers; +using SharpDX; +using SharpDX.Direct3D; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using Vanara.PInvoke; +using Windows.Devices.HumanInterfaceDevice; +using Device = SharpDX.Direct3D11.Device; + +namespace Fischless.GameCapture.DwmSharedSurface +{ + public class SharedSurfaceCapture : IGameCapture + { + private nint _hWnd; + private Device? _d3dDevice; + + public void Dispose() => Stop(); + + public bool IsCapturing { get; private set; } + + private ResourceRegion _region; + + public void Start(nint hWnd) + { + _hWnd = hWnd; + _region = GetGameScreenRegion(hWnd); + _d3dDevice = new Device(SharpDX.Direct3D.DriverType.Hardware, DeviceCreationFlags.BgraSupport); // Software/Hardware + + //var device = Direct3D11Helper.CreateDevice(); + //_d3dDevice = Direct3D11Helper.CreateSharpDXDevice(device); + + IsCapturing = true; + } + + /// + /// 从 GetWindowRect 的带窗口阴影面积矩形 截取出 GetClientRect的矩形(游戏区域) + /// + /// + /// + private ResourceRegion GetGameScreenRegion(nint hWnd) + { + ResourceRegion region = new(); + User32.GetWindowRect(hWnd, out var windowWithShadowRect); + DwmApi.DwmGetWindowAttribute(hWnd, DwmApi.DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, out var windowRect); + User32.GetClientRect(_hWnd, out var clientRect); + + region.Left = windowRect.Left - windowWithShadowRect.Left; + // 标题栏 windowRect.Height - clientRect.Height 上阴影 windowRect.Top - windowWithShadowRect.Top + region.Top = windowRect.Height - clientRect.Height + windowRect.Top - windowWithShadowRect.Top; + region.Right = region.Left + clientRect.Width; + region.Bottom = region.Top + clientRect.Height; + region.Front = 0; + region.Back = 1; + + return region; + } + + public Bitmap? Capture() + { + if (_hWnd == nint.Zero) + { + return null; + } + + + NativeMethods.DwmGetDxSharedSurface(_hWnd, out var phSurface, out _, out _, out _, out _); + if (phSurface == nint.Zero) + { + return null; + } + + return ToBitmap(phSurface); + } + + private Bitmap? ToBitmap(nint phSurface) + { + if (_d3dDevice == null) + { + Debug.WriteLine("D3Device is null."); + return null; + } + + using var surfaceTexture = _d3dDevice.OpenSharedResource(phSurface); + + var staging = new Texture2D(_d3dDevice, new Texture2DDescription + { + Width = _region.Right - _region.Left, + Height = _region.Bottom - _region.Top, + MipLevels = 1, + ArraySize = 1, + Format = Format.B8G8R8A8_UNorm, + Usage = ResourceUsage.Staging, + SampleDescription = new SampleDescription(1, 0), + BindFlags = BindFlags.None, + CpuAccessFlags = CpuAccessFlags.Read, + OptionFlags = ResourceOptionFlags.None + }); + + + return staging.CreateBitmap(_d3dDevice, surfaceTexture, _region); + } + + public void Stop() + { + _hWnd = nint.Zero; + IsCapturing = false; + } + } +} \ No newline at end of file diff --git a/Fischless.WindowCapture/Fischless.WindowCapture.csproj b/Fischless.GameCapture/Fischless.GameCapture.csproj similarity index 100% rename from Fischless.WindowCapture/Fischless.WindowCapture.csproj rename to Fischless.GameCapture/Fischless.GameCapture.csproj diff --git a/Fischless.WindowCapture/WindowCaptureFactory.cs b/Fischless.GameCapture/GameCaptureFactory.cs similarity index 64% rename from Fischless.WindowCapture/WindowCaptureFactory.cs rename to Fischless.GameCapture/GameCaptureFactory.cs index 41179156..0bedc1d9 100644 --- a/Fischless.WindowCapture/WindowCaptureFactory.cs +++ b/Fischless.GameCapture/GameCaptureFactory.cs @@ -1,18 +1,19 @@ -namespace Fischless.WindowCapture; +namespace Fischless.GameCapture; -public class WindowCaptureFactory +public class GameCaptureFactory { public static string[] ModeNames() { return Enum.GetNames(typeof(CaptureModes)); } - public static IWindowCapture Create(CaptureModes mode) + public static IGameCapture Create(CaptureModes mode) { return mode switch { CaptureModes.BitBlt => new BitBlt.BitBltCapture(), CaptureModes.WindowsGraphicsCapture => new Graphics.GraphicsCapture(), + CaptureModes.DwmGetDxSharedSurface => new DwmSharedSurface.SharedSurfaceCapture(), _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, null), }; } diff --git a/Fischless.WindowCapture/Graphics/GraphicsCapture.cs b/Fischless.GameCapture/Graphics/GraphicsCapture.cs similarity index 82% rename from Fischless.WindowCapture/Graphics/GraphicsCapture.cs rename to Fischless.GameCapture/Graphics/GraphicsCapture.cs index afef2eb0..c1d770af 100644 --- a/Fischless.WindowCapture/Graphics/GraphicsCapture.cs +++ b/Fischless.GameCapture/Graphics/GraphicsCapture.cs @@ -1,18 +1,13 @@ -using System.Diagnostics; -using System.Windows; +using Fischless.GameCapture.Graphics.Helpers; +using SharpDX.Direct3D11; +using System.Diagnostics; using Vanara.PInvoke; using Windows.Graphics.Capture; using Windows.Graphics.DirectX; -using Fischless.WindowCapture.Graphics.Helpers; -using SharpDX.Direct3D11; -using System.Windows.Controls; -using static System.Windows.Forms.VisualStyles.VisualStyleElement; -using System.Drawing; -using static Vanara.PInvoke.Gdi32; -namespace Fischless.WindowCapture.Graphics; +namespace Fischless.GameCapture.Graphics; -public class GraphicsCapture : IWindowCapture +public class GraphicsCapture : IGameCapture { private nint _hWnd; @@ -21,7 +16,6 @@ public class GraphicsCapture : IWindowCapture private GraphicsCaptureSession _captureSession = null!; public bool IsCapturing { get; private set; } - public bool IsClientEnabled { get; set; } = false; private ResourceRegion _region; @@ -31,10 +25,7 @@ public class GraphicsCapture : IWindowCapture { _hWnd = hWnd; - if (!IsClientEnabled) - { - _region = GetGameScreenRegion(hWnd); - } + _region = GetGameScreenRegion(hWnd); IsCapturing = true; @@ -58,6 +49,11 @@ public class GraphicsCapture : IWindowCapture IsCapturing = true; } + /// + /// 从 DwmGetWindowAttribute 的矩形 截取出 GetClientRect的矩形(游戏区域) + /// + /// + /// private ResourceRegion GetGameScreenRegion(nint hWnd) { ResourceRegion region = new(); @@ -102,11 +98,6 @@ public class GraphicsCapture : IWindowCapture return null; } - public Bitmap? Capture(int x, int y, int width, int height) - { - return null; - } - public void Stop() { _captureSession?.Dispose(); diff --git a/Fischless.WindowCapture/Graphics/Helpers/CaptureHelper.cs b/Fischless.GameCapture/Graphics/Helpers/CaptureHelper.cs similarity index 96% rename from Fischless.WindowCapture/Graphics/Helpers/CaptureHelper.cs rename to Fischless.GameCapture/Graphics/Helpers/CaptureHelper.cs index 61707aa9..c3112de9 100644 --- a/Fischless.WindowCapture/Graphics/Helpers/CaptureHelper.cs +++ b/Fischless.GameCapture/Graphics/Helpers/CaptureHelper.cs @@ -2,7 +2,7 @@ using Windows.Graphics.Capture; using WinRT; -namespace Fischless.WindowCapture.Graphics; +namespace Fischless.GameCapture.Graphics.Helpers; public static class CaptureHelper { diff --git a/Fischless.WindowCapture/Graphics/Helpers/Direct3D11Helper.cs b/Fischless.GameCapture/Graphics/Helpers/Direct3D11Helper.cs similarity index 97% rename from Fischless.WindowCapture/Graphics/Helpers/Direct3D11Helper.cs rename to Fischless.GameCapture/Graphics/Helpers/Direct3D11Helper.cs index 6a5fc5ac..e312f8a6 100644 --- a/Fischless.WindowCapture/Graphics/Helpers/Direct3D11Helper.cs +++ b/Fischless.GameCapture/Graphics/Helpers/Direct3D11Helper.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; using Windows.Graphics.DirectX.Direct3D11; using WinRT; -namespace Fischless.WindowCapture.Graphics; +namespace Fischless.GameCapture.Graphics.Helpers; [SuppressMessage("CodeQuality", "IDE0052:")] public static class Direct3D11Helper @@ -99,7 +99,7 @@ public static class Direct3D11Helper public static SharpDX.Direct3D11.Device CreateSharpDXDevice(IDirect3DDevice device) { - var access = (IDirect3DDxgiInterfaceAccess)device; + var access = device.As(); var d3dPointer = access.GetInterface(ID3D11Device); var d3dDevice = new SharpDX.Direct3D11.Device(d3dPointer); return d3dDevice; diff --git a/Fischless.WindowCapture/Graphics/Helpers/Texture2DExtensions.cs b/Fischless.GameCapture/Graphics/Helpers/Texture2DExtensions.cs similarity index 81% rename from Fischless.WindowCapture/Graphics/Helpers/Texture2DExtensions.cs rename to Fischless.GameCapture/Graphics/Helpers/Texture2DExtensions.cs index 81bb36cb..4301a7c1 100644 --- a/Fischless.WindowCapture/Graphics/Helpers/Texture2DExtensions.cs +++ b/Fischless.GameCapture/Graphics/Helpers/Texture2DExtensions.cs @@ -4,8 +4,9 @@ using Windows.Graphics.Capture; using SharpDX; using SharpDX.Direct3D11; using SharpDX.DXGI; +using Windows.Devices.HumanInterfaceDevice; -namespace Fischless.WindowCapture.Graphics.Helpers; +namespace Fischless.GameCapture.Graphics.Helpers; public static class Texture2DExtensions { @@ -30,16 +31,21 @@ public static class Texture2DExtensions OptionFlags = ResourceOptionFlags.None }); + return staging.CreateBitmap(d3dDevice, texture2dBitmap, region); + } + + public static Bitmap? CreateBitmap(this Texture2D staging, SharpDX.Direct3D11.Device d3dDevice, Texture2D surfaceTexture, ResourceRegion? region = null) + { try { // Copy data if (region != null) { - d3dDevice.ImmediateContext.CopySubresourceRegion(texture2dBitmap, 0, region, staging, 0); + d3dDevice.ImmediateContext.CopySubresourceRegion(surfaceTexture, 0, region, staging, 0); } else { - d3dDevice.ImmediateContext.CopyResource(texture2dBitmap, staging); + d3dDevice.ImmediateContext.CopyResource(surfaceTexture, staging); } var dataBox = d3dDevice.ImmediateContext.MapSubresource(staging, 0, 0, MapMode.Read, diff --git a/Fischless.WindowCapture/Graphics/Helpers/WinRT.cs b/Fischless.GameCapture/Graphics/Helpers/WinRT.cs similarity index 98% rename from Fischless.WindowCapture/Graphics/Helpers/WinRT.cs rename to Fischless.GameCapture/Graphics/Helpers/WinRT.cs index 4b06d7f8..42df33f4 100644 --- a/Fischless.WindowCapture/Graphics/Helpers/WinRT.cs +++ b/Fischless.GameCapture/Graphics/Helpers/WinRT.cs @@ -3,7 +3,7 @@ using System.Reflection; using System.Runtime.InteropServices; using WinRT; -namespace Fischless.WindowCapture.Graphics; +namespace Fischless.GameCapture.Graphics; #pragma warning disable CS0649 diff --git a/Fischless.GameCapture/IGameCapture.cs b/Fischless.GameCapture/IGameCapture.cs new file mode 100644 index 00000000..b0c08df4 --- /dev/null +++ b/Fischless.GameCapture/IGameCapture.cs @@ -0,0 +1,12 @@ +namespace Fischless.GameCapture; + +public interface IGameCapture : IDisposable +{ + public bool IsCapturing { get; } + + public void Start(nint hWnd); + + public Bitmap? Capture(); + + public void Stop(); +} diff --git a/Fischless.WindowCapture/BitBlt/BitBltCapture.cs b/Fischless.WindowCapture/BitBlt/BitBltCapture.cs deleted file mode 100644 index 4c3bbf26..00000000 --- a/Fischless.WindowCapture/BitBlt/BitBltCapture.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Diagnostics; -using Vanara.PInvoke; - -namespace Fischless.WindowCapture.BitBlt; - -public class BitBltCapture : IWindowCapture -{ - private nint _hWnd; - public bool IsCapturing { get; private set; } - public bool IsClientEnabled { get; set; } = false; - public bool IsCursorCaptureEnabled { get; set; } = false; - - public void Dispose() - { - Stop(); - } - - public void Start(nint hWnd) - { - _hWnd = hWnd; - IsCapturing = true; - } - - public Bitmap? Capture() - { - if (_hWnd == IntPtr.Zero) - { - return null; - } - - try - { - if (IsClientEnabled) - { - _ = DwmApi.DwmGetWindowAttribute(_hWnd, DwmApi.DWMWINDOWATTRIBUTE.DWMWA_EXTENDED_FRAME_BOUNDS, out var windowRect); - var width = windowRect.right - windowRect.left; - var height = windowRect.bottom - windowRect.top; - - var hdcSrc = User32.GetWindowDC(_hWnd); - var hdcDest = Gdi32.CreateCompatibleDC(hdcSrc); - var hBitmap = Gdi32.CreateCompatibleBitmap(hdcSrc, width, height); - var hOld = Gdi32.SelectObject(hdcDest, hBitmap); - _ = Gdi32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, Gdi32.RasterOperationMode.SRCCOPY); - _ = Gdi32.SelectObject(hdcDest, hOld); - _ = Gdi32.DeleteDC(hdcDest); - _ = User32.ReleaseDC(_hWnd, hdcSrc); - - var bitmap = hBitmap.ToBitmap(); - _ = Gdi32.DeleteObject(hBitmap); - return bitmap; - } - else - { - _ = User32.GetClientRect(_hWnd, out var windowRect); - int x = default, y = default; - int width = windowRect.right - windowRect.left; - int height = windowRect.bottom - windowRect.top; - - Bitmap bitmap = new(width, height); - using System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bitmap); - nint hdcDest = g.GetHdc(); - Gdi32.SafeHDC hdcSrc = User32.GetDC(_hWnd == IntPtr.Zero ? User32.GetDesktopWindow() : _hWnd); - _ = Gdi32.StretchBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, width, height, Gdi32.RasterOperationMode.SRCCOPY); - g.ReleaseHdc(); - _ = Gdi32.DeleteDC(hdcDest); - _ = Gdi32.DeleteDC(hdcSrc); - return bitmap; - } - } - catch (Exception e) - { - Debug.WriteLine(e); - } - return null!; - } - - public Bitmap? Capture(int x, int y, int width, int height) - { - if (_hWnd == IntPtr.Zero) - { - return null; - } - - if (IsClientEnabled) - { - throw new NotSupportedException(); - } - - try - { - Bitmap copied = new(width, height); - using System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(copied); - nint hdcDest = g.GetHdc(); - Gdi32.SafeHDC hdcSrc = User32.GetDC(_hWnd == IntPtr.Zero ? User32.GetDesktopWindow() : _hWnd); - _ = Gdi32.StretchBlt(hdcDest, 0, 0, width, height, hdcSrc, x, y, width, height, Gdi32.RasterOperationMode.SRCCOPY); - g.ReleaseHdc(); - _ = Gdi32.DeleteDC(hdcDest); - _ = Gdi32.DeleteDC(hdcSrc); - return copied; - } - catch (Exception e) - { - Debug.WriteLine(e); - } - return null; - } - - public void Stop() - { - _hWnd = IntPtr.Zero; - IsCapturing = false; - } -} diff --git a/Fischless.WindowCapture/IWindowCapture.cs b/Fischless.WindowCapture/IWindowCapture.cs deleted file mode 100644 index 21f741a8..00000000 --- a/Fischless.WindowCapture/IWindowCapture.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Fischless.WindowCapture; - -public interface IWindowCapture : IDisposable -{ - public bool IsCapturing { get; } - public bool IsClientEnabled { get; set; } - - public void Start(nint hWnd); - - public Bitmap? Capture(); - public Bitmap? Capture(int x, int y, int width, int height); - - public void Stop(); -}