diff --git a/Core.Window/ClipboardWindow.cs b/Core.Window/ClipboardWindow.cs index 0d0f3d1..c1944bb 100644 --- a/Core.Window/ClipboardWindow.cs +++ b/Core.Window/ClipboardWindow.cs @@ -113,54 +113,7 @@ public bool HasImage() thread.Start(); tcs.Task.Wait(); return writeableBitmap; - // var writeableBitmap = new WriteableBitmap(); - // bitmapSource.CopyPixels(); - try - { - if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime appLifetime) - { - byte[] result = []; - Dispatcher.UIThread.Invoke(() => - { - result = appLifetime.MainWindow.Clipboard.GetDataAsync("Unknown_Format_2") - .WaitAsync(TimeSpan.FromSeconds(1)) - .GetAwaiter() - .GetResult() as byte[]; - }); - var imgInfo = new byte[Marshal.SizeOf()]; - var img = new byte[result.Length - Marshal.SizeOf() + 4]; - Array.Copy(result, 40, img, 0, img.Length); - Array.Copy(result, 0, imgInfo, 0, imgInfo.Length); - var info = BytesToStructure(imgInfo); - var configuration = Configuration.Default; - configuration.PreferContiguousImageBuffers = true; - - var image = Image.LoadPixelData(configuration, img, info.bmiHeader.biWidth, - info.bmiHeader.biHeight); - image.Mutate(x => x.Flip(FlipMode.Vertical)); - if (!image.DangerousTryGetSinglePixelMemory(out var memory)) - throw new Exception( - "This can only happen with multi-GB images or when PreferContiguousImageBuffers is not set to true."); - - using (var pinHandle = memory.Pin()) - { - unsafe - { - var pixelFormat = PixelFormat.Bgra8888; - var bitmap1 = new Bitmap(pixelFormat, AlphaFormat.Unpremul, (IntPtr)pinHandle.Pointer, - new PixelSize(info.bmiHeader.biWidth, info.bmiHeader.biHeight), new Vector(96, 96), - ((info.bmiHeader.biWidth * pixelFormat.BitsPerPixel + 31) & ~31) >> 3); - return bitmap1; - } - } - } - } - catch (Exception e) - { - return null; - } - - return null; + } public bool SetImage(Bitmap image) diff --git a/Core.Window/ScreenCapture/CaptureTool.cs b/Core.Window/ScreenCapture/CaptureTool.cs index 138dd31..a9c5a70 100644 --- a/Core.Window/ScreenCapture/CaptureTool.cs +++ b/Core.Window/ScreenCapture/CaptureTool.cs @@ -1,4 +1,5 @@ -using PluginCore; +using System.Buffers; +using PluginCore; using Silk.NET.Direct3D11; using Silk.NET.DXGI; @@ -29,7 +30,7 @@ public static unsafe byte[] GetBytesSpan(MappedSubresource mappedSubresource, Ou // 结果数组:区域宽 * 区域高 * 4(RGBA) int regionWidth = endX - startX; int regionHeight = endY - startY; - byte[] result = new byte[regionWidth * regionHeight * 4]; + byte[] result =ArrayPool.Shared.Rent(regionWidth * regionHeight * 4); if (!outputDesc.ColorSpace.ToString().EndsWith("2020")) { var span = new ReadOnlySpan(mappedSubresource.PData, diff --git a/Core.Window/ScreenCapture/ScreenCaptureByDx11.cs b/Core.Window/ScreenCapture/ScreenCaptureByDx11.cs index 0ea9698..60da77c 100644 --- a/Core.Window/ScreenCapture/ScreenCaptureByDx11.cs +++ b/Core.Window/ScreenCapture/ScreenCaptureByDx11.cs @@ -1,4 +1,5 @@ -using System.Collections; +using System.Buffers; +using System.Collections; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -421,7 +422,7 @@ public Stack CaptureAllScreenBitmap() } } - captureAllScreenInfo.Bytes = null; + ArrayPool.Shared.Return(captureAllScreenInfo.Bytes); captureAllScreenInfo.Source = writeableBitmap; screenCaptureResults.Push(captureAllScreenInfo); } diff --git a/Core.Window/ScreenCapture/ScreenCaptureByWGC.cs b/Core.Window/ScreenCapture/ScreenCaptureByWGC.cs index f9fd093..55b66b7 100644 --- a/Core.Window/ScreenCapture/ScreenCaptureByWGC.cs +++ b/Core.Window/ScreenCapture/ScreenCaptureByWGC.cs @@ -1,4 +1,5 @@ -using System.Diagnostics; +using System.Buffers; +using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.InteropServices.WindowsRuntime; using System.Text; @@ -260,7 +261,7 @@ public ScreenCaptureResult CaptureScreenBitmap(ScreenCaptureResult captureAllScr } } - captureAllScreenInfo.Bytes = null; + ArrayPool.Shared.Return(captureAllScreenInfo.Bytes); captureAllScreenInfo.Source = writeableBitmap; return captureAllScreenInfo; } diff --git a/KitopiaAvalonia/Services/ScreenCaptureWindow.cs b/KitopiaAvalonia/Services/ScreenCaptureWindow.cs index ae7367d..3924122 100644 --- a/KitopiaAvalonia/Services/ScreenCaptureWindow.cs +++ b/KitopiaAvalonia/Services/ScreenCaptureWindow.cs @@ -20,8 +20,8 @@ public void CaptureScreen() var results = ServiceManager.Services.GetService()!.CaptureAllScreenBitmap(); while (results.TryPop(out var result)) { - var window = new Windows.ScreenCaptureWindow(result.Info); - window.Image.Source = result.Source; + var window = new Windows.ScreenCaptureWindow(result); + window.Show(); } @@ -33,8 +33,8 @@ public void RequestUserSelectScreenInfo(Action action) var results = ServiceManager.Services.GetService()!.CaptureAllScreenBitmap(); while (results.TryPop(out var result)) { - var window = new Windows.ScreenCaptureWindow(result.Info); - window.Image.Source = result.Source; + var window = new Windows.ScreenCaptureWindow(result); + window.SetToSelectMode(action.Invoke); window.Show(); } @@ -50,8 +50,8 @@ public void RequestUserSelectScreenBytes(Action action,Acti Lock @lock = new Lock(); while (results.TryPop(out var result)) { - var window = new Windows.ScreenCaptureWindow(result.Info); - window.Image.Source = result.Source; + var window = new Windows.ScreenCaptureWindow(result); + window.SetToSelectBytesMode(action.Invoke, (() => { lock (@lock) diff --git a/KitopiaAvalonia/Windows/ScreenCaptureWindow.axaml.cs b/KitopiaAvalonia/Windows/ScreenCaptureWindow.axaml.cs index 1a0c28e..0aec65c 100644 --- a/KitopiaAvalonia/Windows/ScreenCaptureWindow.axaml.cs +++ b/KitopiaAvalonia/Windows/ScreenCaptureWindow.axaml.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -51,12 +52,13 @@ public partial class ScreenCaptureWindow : Window private bool Finish = false; private List _windowInfos; private WindowInfo _currentWindowInfo; - public ScreenCaptureWindow(ScreenCaptureInfo screenCaptureInfo) + public ScreenCaptureWindow(ScreenCaptureResult screenCaptureResult) { InitializeComponent(); _windowInfos = ServiceManager.Services.GetService()!.GetAllWindowInfo(); - _screenCaptureInfo = screenCaptureInfo; - Position = new PixelPoint(screenCaptureInfo.ScreenInfo.X, screenCaptureInfo.ScreenInfo.Y); + _screenCaptureInfo = screenCaptureResult.Info; + Image.Source = screenCaptureResult.Source; + Position = new PixelPoint(screenCaptureResult.Info.ScreenInfo.X, screenCaptureResult.Info.ScreenInfo.Y); WindowState = WindowState.FullScreen; SystemDecorations = SystemDecorations.None; Background = new SolidColorBrush(Colors.Black); @@ -943,7 +945,7 @@ private void FinnishCapture() bufferSize, (((int)cropW * PixelFormat.Rgba8888.BitsPerPixel + 31) & ~31) >> 3 ); - var ys = new byte[bufferSize]; + var ys =ArrayPool.Shared.Rent(bufferSize); Marshal.Copy(ptr, ys, 0, bufferSize); Marshal.FreeHGlobal(ptr); if (selectBytesMode) @@ -965,6 +967,7 @@ private void FinnishCapture() }); }).ContinueWith((e) => { + ArrayPool.Shared.Return(ys); GC.Collect(2,GCCollectionMode.Optimized); }); @@ -987,6 +990,7 @@ private void FinnishCapture() Bytes = ys }).ContinueWith((e) => { + ArrayPool.Shared.Return(ys); GC.Collect(2,GCCollectionMode.Optimized); }); } diff --git a/KitopiaBenchmark/CaptureTool.cs b/KitopiaBenchmark/CaptureTool.cs new file mode 100644 index 0000000..de508bf --- /dev/null +++ b/KitopiaBenchmark/CaptureTool.cs @@ -0,0 +1,113 @@ +using System.Buffers; +using Core.Window; +using PluginCore; +using Silk.NET.Direct3D11; +using Silk.NET.DXGI; + +namespace KitopiaBenchmark; + +public static class CaptureTool +{ + public static unsafe byte[] GetBytesSpan(MappedSubresource mappedSubresource, OutputDesc1 outputDesc,ref ScreenCaptureInfo screenCaptureInfo) + { + var sizeX = outputDesc.DesktopCoordinates.Size.X; + int startX = Math.Clamp(screenCaptureInfo.X, 0, outputDesc.DesktopCoordinates.Size.X - 1); + int startY = Math.Clamp(screenCaptureInfo.Y, 0, outputDesc.DesktopCoordinates.Size.Y - 1); + int endX = Math.Clamp(screenCaptureInfo.X+screenCaptureInfo.Width, 0, outputDesc.DesktopCoordinates.Size.X); + int endY = Math.Clamp(screenCaptureInfo.Y+screenCaptureInfo.Height, 0, outputDesc.DesktopCoordinates.Size.Y); + if (screenCaptureInfo.ScreenCaptureType==ScreenCaptureType.窗口) + { + sizeX=(screenCaptureInfo.WindowInfo.Rect.Width + 3) & ~3; + startX = 0; + startY = 0; + endX = (screenCaptureInfo.WindowInfo.Rect.Width + 3) & ~3; + endY = (int)screenCaptureInfo.WindowInfo.Rect.Height; + } + + screenCaptureInfo.Height = endY - startY; + screenCaptureInfo.Width = endX - startX; + screenCaptureInfo.X = startX; + screenCaptureInfo.Y = startY; + // 结果数组:区域宽 * 区域高 * 4(RGBA) + int regionWidth = endX - startX; + int regionHeight = endY - startY; + byte[] result = new byte[regionWidth * regionHeight * 4]; + if (!outputDesc.ColorSpace.ToString().EndsWith("2020")) + { + var span = new ReadOnlySpan(mappedSubresource.PData, + (int)mappedSubresource.DepthPitch / 4); + + for (int y = startY; y < endY; y++) + { + for (int x = startX; x < endX; x++) + { + int sourceIndex = (y * sizeX + x) * 4; + int targetIndex = ((y - startY) * regionWidth + (x - startX)) * 4; + + // 读取原始像素并复制到结果 + uint value = span[sourceIndex / 4]; + result[targetIndex+ 2] = (byte)(value & 0xFF); // R + result[targetIndex + 1] = (byte)((value >> 8) & 0xFF); // G + result[targetIndex ] = (byte)((value >> 16) & 0xFF); // B + result[targetIndex + 3] = (byte)((value >> 24) & 0xFF); // A + } + } + } + else + { + var matrix = ColorSpaceCtr.CtrColorSpace([ + outputDesc.RedPrimary[0], + outputDesc.RedPrimary[1], + outputDesc.GreenPrimary[0], + outputDesc.GreenPrimary[1], + outputDesc.BluePrimary[0], + outputDesc.BluePrimary[1], + outputDesc.WhitePoint[0], + outputDesc.WhitePoint[1] + ], + [ + .640f, .330f, .300f, .600f, .150f, .060f, .3127f, + .3290f + ] + ); + var span = new ReadOnlySpan(mappedSubresource.PData, + (int)mappedSubresource.DepthPitch / 2).ToArray(); + // ReadOnlyMemory readOnlyMemory = new ReadOnlyMemory(span); + Parallel.For(startY, endY, y => + //for (int y = startY; y < endY; y++) + { + + int yOffset = y * sizeX; + int targetYOffset = (y - startY) * regionWidth; + + for (int x = startX; x < endX; x++) + { + int sourceIndex = (yOffset + x) * 4; + int targetIndex = (targetYOffset + (x - startX)) * 4; + + // 读取并归一化 RGBA 值 + + float r = float.Log(1 + (float)span[sourceIndex]) / 1.749199854809259f; + float g = float.Log(1 + (float)span[sourceIndex+1]) / 1.749199854809259f; + float b = float.Log(1 + (float)span[sourceIndex+2]) / 1.749199854809259f; + + // 应用色彩转换矩阵 + float bt2020R = matrix[0, 0] * r + matrix[0, 1] * g + matrix[0, 2] * b; + float bt2020G = matrix[1, 0] * r + matrix[1, 1] * g + matrix[1, 2] * b; + float bt2020B = matrix[2, 0] * r + matrix[2, 1] * g + matrix[2, 2] * b; + + // 转换并填充结果 + + result[targetIndex ] = (byte)Math.Clamp(bt2020B * 255,0,255); + result[targetIndex + 1] = (byte)Math.Clamp(bt2020G * 255,0,255); + result[targetIndex+ 2] = (byte)Math.Clamp(bt2020R * 255,0,255); + result[targetIndex + 3] = 255; // Alpha 固定为 255 + } + } + ); + + } + + return result; + } +} \ No newline at end of file diff --git a/KitopiaBenchmark/KitopiaBenchmark.csproj b/KitopiaBenchmark/KitopiaBenchmark.csproj index 56126f7..21fb8e2 100644 --- a/KitopiaBenchmark/KitopiaBenchmark.csproj +++ b/KitopiaBenchmark/KitopiaBenchmark.csproj @@ -5,15 +5,24 @@ enable enable + true - net8.0 + net9.0 - net8.0-windows10.0.17763.0 + net9.0-windows10.0.19041.0 + win-x86;win-x64;win-arm64 + None + true + + + + + diff --git a/KitopiaBenchmark/Program.cs b/KitopiaBenchmark/Program.cs index a2e087a..3a09494 100644 --- a/KitopiaBenchmark/Program.cs +++ b/KitopiaBenchmark/Program.cs @@ -6,6 +6,6 @@ internal class Program { private static void Main(string[] args) { - BenchmarkRunner.Run(); + BenchmarkRunner.Run(); } } \ No newline at end of file diff --git a/KitopiaBenchmark/ScreenCapture.cs b/KitopiaBenchmark/ScreenCapture.cs new file mode 100644 index 0000000..e9bd916 --- /dev/null +++ b/KitopiaBenchmark/ScreenCapture.cs @@ -0,0 +1,56 @@ +using System.Buffers; +using BenchmarkDotNet.Attributes; +using Core.Window; +using PluginCore; + +namespace KitopiaBenchmark; +[MemoryDiagnoser] +public class ScreenCapture +{ + private ScreenCaptureByWGC _screenCaptureByWgc; + private ScreenCaptureByWGCWithoutPool _screenCaptureByWgcWithoutPool; + [GlobalSetup] + public void Setup() + { + _screenCaptureByWgc = new ScreenCaptureByWGC(); + _screenCaptureByWgcWithoutPool = new ScreenCaptureByWGCWithoutPool(); + } + [Benchmark] + public void WithoutPoolButDontSetNull() + { + var captureAllScreenBytes = _screenCaptureByWgcWithoutPool.CaptureAllScreenBytes(); + while (captureAllScreenBytes.TryPop(out var result)) + { + //result.Bytes = null; + // ArrayPool.Shared.Return(result.Bytes); + } + } + [Benchmark] + public void WithoutPool() + { + var captureAllScreenBytes = _screenCaptureByWgcWithoutPool.CaptureAllScreenBytes(); + while (captureAllScreenBytes.TryPop(out var result)) + { + result.Bytes = null; + // ArrayPool.Shared.Return(result.Bytes); + } + } + [Benchmark] + public void PoolButDontReturn() + { + var captureAllScreenBytes = _screenCaptureByWgc.CaptureAllScreenBytes(); + while (captureAllScreenBytes.TryPop(out var result)) + { + // ArrayPool.Shared.Return(result.Bytes); + } + } + [Benchmark] + public void Pool() + { + var captureAllScreenBytes = _screenCaptureByWgc.CaptureAllScreenBytes(); + while (captureAllScreenBytes.TryPop(out var result)) + { + ArrayPool.Shared.Return(result.Bytes); + } + } +} \ No newline at end of file diff --git a/KitopiaBenchmark/ScreenCaptureByWGC.cs b/KitopiaBenchmark/ScreenCaptureByWGC.cs new file mode 100644 index 0000000..8e1f202 --- /dev/null +++ b/KitopiaBenchmark/ScreenCaptureByWGC.cs @@ -0,0 +1,547 @@ +using System.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; +using System.Text; +using Windows.Graphics; +using Windows.Graphics.Capture; +using Avalonia; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using PluginCore; +using Silk.NET.Core.Contexts; +using Silk.NET.Core.Native; +using Silk.NET.Direct3D11; +using Silk.NET.DXGI; +using Vanara.PInvoke; +using WinRT; +using DirectXPixelFormat = Windows.Graphics.DirectX.DirectXPixelFormat; +using IDirect3DDevice = Windows.Graphics.DirectX.Direct3D11.IDirect3DDevice; +using IDirect3DSurface = Windows.Graphics.DirectX.Direct3D11.IDirect3DSurface; +using IInspectable = WinRT.IInspectable; +using Rect = PluginCore.Rect; + +namespace KitopiaBenchmark; + +public class ScreenCaptureByWGCWithoutPool : IScreenCapture +{ + public List GetAllScreenInfo() + { + var screenCaptureInfos = new List(); + uint i = 0; + User32.EnumDisplayMonitors(default, null, (arg1, arg2, arg3, arg4) => + { + screenCaptureInfos.Add(new ScreenCaptureInfo() + { + + X = 0, + Y = 0, + Width = arg3.right - arg3.left, + Height = arg3.bottom - arg3.top, + ScreenInfo = new ScreenInfo() + { + X = arg3.left, + Y = arg3.top, + Width = arg3.right - arg3.left, + Height = arg3.bottom - arg3.top, + hMonitor = arg1, + + } + + }); + i++; + return true; + }, IntPtr.Zero); + + return screenCaptureInfos; + } + public const uint WS_POPUP = 0x80000000; // 弹出窗口样式 + public const uint WS_CHILD = 0x40000000; // 子窗口样式 + private const int WS_EX_TOOLWINDOW = 0x00000080; // 工具窗口 + private const int WS_EX_APPWINDOW = 0x00040000; // 应用窗口 + const int WS_EX_NOREDIRECTIONBITMAP = 0x00200000; + public List GetAllWindowInfo() + { + var screenCaptureInfos = new List(); + HWND currentHwnd = User32.GetTopWindow(IntPtr.Zero); + int zIndex = 0; + + while (currentHwnd != IntPtr.Zero) + { + + zIndex++; + currentHwnd = User32.GetWindow(currentHwnd, User32.GetWindowCmd.GW_HWNDNEXT); + // 忽略有父窗口的和不可见的窗口 + if (!User32.GetParent(currentHwnd).IsNull || !User32.IsWindowVisible(currentHwnd)) + { + continue; + } + int style1= User32.GetWindowLong(currentHwnd,User32.WindowLongFlags.GWL_STYLE); + if ( (style1 & (uint) User32.WindowStyles.WS_POPUP) != 0) + { + continue; + } + int style2 = User32.GetWindowLong(currentHwnd,User32.WindowLongFlags.GWL_EXSTYLE); + if ( (style2 & (int) User32.WindowStylesEx.WS_EX_NOACTIVATE) != 0||(style2 & (int) User32.WindowStylesEx.WS_EX_TOOLWINDOW) != 0) + { + continue; + } + if (!User32.IsWindow(currentHwnd)) + { + continue; + } + + User32.GetWindowDisplayAffinity(currentHwnd, out var affinity); + if (affinity!= User32.WindowDisplayAffinity.WDA_NONE) + { + continue; + } + User32.GetWindowThreadProcessId(currentHwnd,out var id); + + var s = Process.GetProcessById((int)id).ProcessName; + + + + // 获取窗口标题 + StringBuilder stringBuilder = new StringBuilder(100); + User32.GetWindowText(currentHwnd, stringBuilder, 100); + var title = stringBuilder.ToString(); + if (string.IsNullOrWhiteSpace(title)) + { + continue; + } + // 获取窗口的位置和大小 + User32.GetWindowRect(currentHwnd, out var clientRect); + + //User32.GetClientRect(currentHwnd, out RECT clientRect); + var hMonitor = User32.MonitorFromWindow(currentHwnd, User32.MonitorFlags.MONITOR_DEFAULTTONEAREST); + + // 获取监视器信息(包括屏幕工作区域) + User32.MONITORINFO monitorInfo = new User32.MONITORINFO { cbSize = (uint)Marshal.SizeOf(typeof(User32.MONITORINFO)) }; + User32.GetMonitorInfo(hMonitor, ref monitorInfo); + + RECT screenRect = monitorInfo.rcWork; + + // 计算窗口与屏幕的可见区域交集 + RECT visibleRect = IntersectRects(clientRect, screenRect); + + if (visibleRect.Width > 0 && visibleRect.Height > 0) + { + screenCaptureInfos.Add(new WindowInfo() + { + Title = title, + ModuleFileName = s, + Hwnd = currentHwnd.DangerousGetHandle(), + Rect = new Rect( visibleRect.X, visibleRect.Y, visibleRect.Width, visibleRect.Height), + ZIndex = zIndex + }); + } + + } + + // 添加到结果列表 + + + + + + return screenCaptureInfos; + } + static RECT IntersectRects(RECT rect1, RECT rect2) + { + RECT result = new RECT + { + Left = Math.Max(rect1.Left, rect2.Left), + Top = Math.Max(rect1.Top, rect2.Top), + Right = Math.Min(rect1.Right, rect2.Right), + Bottom = Math.Min(rect1.Bottom, rect2.Bottom) + }; + + // 如果没有交集 + if (result.Right < result.Left || result.Bottom < result.Top) + { + result = new RECT { Left = 0, Top = 0, Right = 0, Bottom = 0 }; + } + + return result; + } + public ScreenCaptureInfo GetScreenCaptureInfoByIndex(int index) + { + return default; + } + + private static readonly Guid GraphicsCaptureItemGuid = new("79C3F95B-31F7-4EC2-A464-632EF5D30760"); + + [ComImport] + [Guid("3628E81B-3CAC-4C60-B7F4-23CE0E0C3356")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [ComVisible(true)] + private interface IGraphicsCaptureItemInterop + { + IntPtr CreateForWindow( + [In] IntPtr window, + [In] ref Guid iid); + + IntPtr CreateForMonitor( + [In] IntPtr monitor, + [In] ref Guid iid); + } + + [DllImport( + "d3d11.dll", + EntryPoint = "CreateDirect3D11DeviceFromDXGIDevice", + SetLastError = true, + CharSet = CharSet.Unicode, + ExactSpelling = true, + CallingConvention = CallingConvention.StdCall + )] + private static extern uint CreateDirect3D11DeviceFromDXGIDevice(IntPtr dxgiDevice, out IntPtr graphicsDevice); + + private ComPtr CreateSharpDXTexture2D(IDirect3DSurface surface) + { + unsafe + { + var iInspectable = (IInspectable)(object)surface; + var queryInterface = iInspectable.ObjRef.AsInterface(); + var d3dPointer = (void*)queryInterface.GetInterface(ID3D11Resource.Guid); + var comPtr = new ComPtr(); + comPtr.Handle = (ID3D11Resource*)d3dPointer; + return comPtr; + } + } + + + [ComImport] + [Guid("A9B3D012-3DF2-4EE3-B8D1-8695F457D3C1")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [ComVisible(true)] + private interface IDirect3DDxgiInterfaceAccess + { + IntPtr GetInterface([In] ref Guid iid); + }; + + public Stack CaptureAllScreenBitmap() + { + var screenCaptureResults = new Stack(); + var captureAllScreenBytes = CaptureAllScreenBytes(); + while (captureAllScreenBytes.TryPop(out var captureAllScreenInfo)) + { + screenCaptureResults.Push(CaptureScreenBitmap(captureAllScreenInfo)); + } + + return screenCaptureResults; + } + + public Stack CaptureAllScreenBytes() + { + var screenCaptureResults = new Stack(); + foreach (var screenCaptureInfo in GetAllScreenInfo()) + { + screenCaptureResults.Push(CaptureScreenBytes(screenCaptureInfo)); + } + + return screenCaptureResults; + } + + public ScreenCaptureResult CaptureScreenBitmap(ScreenCaptureResult captureAllScreenInfo) + { + var writeableBitmap = new WriteableBitmap( + new PixelSize(captureAllScreenInfo.Info.Width, captureAllScreenInfo.Info.Height), + new Vector(96, 96), PixelFormat.Bgra8888 ); + using (var l = writeableBitmap.Lock()) + { + unsafe + { + var destinationSizeInBytes = captureAllScreenInfo.Info.Width * 4 * captureAllScreenInfo.Info.Height; + fixed (byte* srcPtr = captureAllScreenInfo.Bytes) + { + Buffer.MemoryCopy(srcPtr,(void*)l.Address,destinationSizeInBytes,destinationSizeInBytes); + } + + } + } + + captureAllScreenInfo.Bytes = null; + captureAllScreenInfo.Source = writeableBitmap; + return captureAllScreenInfo; + } + + public IntPtr FindHMonitor(ScreenInfo screenInfo) + { + IntPtr h=IntPtr.Zero; + User32.EnumDisplayMonitors(default, null, (arg1, arg2, arg3, arg4) => + { + if (screenInfo.X==arg3.left&&screenInfo.Y==arg3.top&&screenInfo.Width==arg3.right-arg3.left&&screenInfo.Height==arg3.bottom-arg3.top) + { + h = arg1; + return false; + } + return true; + }, IntPtr.Zero); + return h; + } + public static (ComPtr,OutputDesc1) GetAdapterForMonitor(ComPtr factory,IntPtr hMonitor) + { + ComPtr adapter = null; + uint i = 0; + while (factory.EnumAdapters1(i, ref adapter) == 0) + { + uint j = 0; + ComPtr output = null; + while (adapter.EnumOutputs(j, ref output ) == 0) + { + OutputDesc desc = new OutputDesc(); + output.GetDesc(ref desc); + if (desc.Monitor == hMonitor) + { + ComPtr output6 = null; + if (output.QueryInterface(out output6) != 0) + throw new Exception("Failed to get IDXGIOutput6"); + var outputDesc = new OutputDesc1(); + if (output6.GetDesc1(ref outputDesc) != 0) throw new Exception("Failed to get Desc1"); + return (adapter,outputDesc); + } + else + { + output.Release(); + } + + j++; + } + + adapter.Release(); + i++; + } + + throw new InvalidOperationException("No adapter found for the given monitor."); + } + + public unsafe ScreenCaptureResult CaptureScreenBytes(ScreenCaptureInfo screenCaptureInfo) + { + switch (screenCaptureInfo.ScreenCaptureType) + { + case ScreenCaptureType.屏幕: + { + if (screenCaptureInfo.ScreenInfo.hMonitor==null || screenCaptureInfo.ScreenInfo.hMonitor ==IntPtr.Zero) + { + screenCaptureInfo.ScreenInfo.hMonitor = FindHMonitor(screenCaptureInfo.ScreenInfo); + if (screenCaptureInfo.ScreenInfo.hMonitor==IntPtr.Zero) + { + throw new Exception("目标显示器不存在"); + } + } + break; + } + case ScreenCaptureType.窗口: + { + var allWindowInfo = GetAllWindowInfo(); + if (allWindowInfo.Any(e=>e.Hwnd==screenCaptureInfo.WindowInfo.Hwnd&&e.Title==screenCaptureInfo.WindowInfo.Title&&e.ModuleFileName==screenCaptureInfo.WindowInfo.ModuleFileName)) + { + var monitorFromWindow = User32.MonitorFromWindow(screenCaptureInfo.WindowInfo.Hwnd,User32.MonitorFlags.MONITOR_DEFAULTTONEAREST); + screenCaptureInfo.ScreenInfo.hMonitor = monitorFromWindow.DangerousGetHandle(); + break; + } + else if (allWindowInfo.Any(e=>e.Title==screenCaptureInfo.WindowInfo.Title&&e.ModuleFileName==screenCaptureInfo.WindowInfo.ModuleFileName)) + { + screenCaptureInfo.WindowInfo=allWindowInfo.First(e=>e.Title==screenCaptureInfo.WindowInfo.Title&&e.ModuleFileName==screenCaptureInfo.WindowInfo.ModuleFileName); + var monitorFromWindow = User32.MonitorFromWindow(screenCaptureInfo.WindowInfo.Hwnd,User32.MonitorFlags.MONITOR_DEFAULTTONEAREST); + screenCaptureInfo.ScreenInfo.hMonitor = monitorFromWindow.DangerousGetHandle(); + break; + }else if (allWindowInfo.Any(e=>e.ModuleFileName==screenCaptureInfo.WindowInfo.ModuleFileName)) + { + screenCaptureInfo.WindowInfo=allWindowInfo.First(e=>e.ModuleFileName==screenCaptureInfo.WindowInfo.ModuleFileName); + var monitorFromWindow = User32.MonitorFromWindow(screenCaptureInfo.WindowInfo.Hwnd,User32.MonitorFlags.MONITOR_DEFAULTTONEAREST); + screenCaptureInfo.ScreenInfo.hMonitor = monitorFromWindow.DangerousGetHandle(); + break; + } + throw new Exception("目标窗口不存在"); + } + + } + + var factory2 = ActivationFactory.Get(typeof(GraphicsCaptureItem).FullName); + var interop = factory2.AsInterface(); + IntPtr itemPointer = IntPtr.Zero; + switch (screenCaptureInfo.ScreenCaptureType) + { + case ScreenCaptureType.屏幕: + { + itemPointer= interop.CreateForMonitor(screenCaptureInfo.ScreenInfo.hMonitor, GraphicsCaptureItemGuid); + break; + } + case ScreenCaptureType.窗口: + { + + itemPointer= interop.CreateForWindow(screenCaptureInfo.WindowInfo.Hwnd, GraphicsCaptureItemGuid); + break; + } + + } + var item = MarshalInterface.FromAbi(itemPointer); + var dxgi = new DXGI(new DefaultNativeContext("dxgi")); + + ComPtr adapter1 = null; + ID3D11DeviceContext* context = null; + ID3D11DeviceContext* immediateContext = null; + ID3D11Device* d3dDevice = null; + ComPtr stagingResource = null; + Direct3D11CaptureFrame direct3D11CaptureFrame = null; + ID3D11Texture2D* stagingTexture = null; + GraphicsCaptureSession session = null; + Direct3D11CaptureFramePool framePool = null; + using var d3D11 = new D3D11(new DefaultNativeContext("d3d11")); + try + { + using var factory = dxgi.CreateDXGIFactory1(); + var adapterForMonitor = GetAdapterForMonitor(factory,screenCaptureInfo.ScreenInfo.hMonitor); + adapter1 = adapterForMonitor.Item1; + + var featureLevel = D3DFeatureLevel.Level110; + D3DFeatureLevel[] featureLevels = + [ + D3DFeatureLevel.Level110 + ]; + fixed (D3DFeatureLevel* pFeatureLevels = &featureLevels[0]) + { + if (d3D11.CreateDevice((IDXGIAdapter*)adapter1.Handle, D3DDriverType.Unknown, IntPtr.Zero, + (uint)CreateDeviceFlag.None, pFeatureLevels, (uint)featureLevels.Length, D3D11.SdkVersion, + ref d3dDevice, + &featureLevel, ref context) != 0) + throw new Exception("Failed to create D3D11 device"); + } + + d3dDevice->GetImmediateContext(ref immediateContext); + + IDirect3DDevice CreateDirect3DDeviceFromSharpDXDevice(ID3D11Device* d3dDevice) + { + IDirect3DDevice device = null; + + // Acquire the DXGI interface for the Direct3D device. + using (var dxgiDevice = d3dDevice->QueryInterface()) + { + // Wrap the native device using a WinRT interop object. + var hr = CreateDirect3D11DeviceFromDXGIDevice((IntPtr)dxgiDevice.Handle, out var pUnknown); + + if (hr == 0) + { + device = MarshalInterface.FromAbi(pUnknown); + Marshal.Release(pUnknown); + } + } + + return device; + } + + var direct3DDeviceFromSharpDxDevice = CreateDirect3DDeviceFromSharpDXDevice(d3dDevice); + + framePool = Direct3D11CaptureFramePool.Create( + direct3DDeviceFromSharpDxDevice, + adapterForMonitor.Item2.ColorSpace.ToString().EndsWith("2020") + ? DirectXPixelFormat.R16G16B16A16Float + : DirectXPixelFormat.R8G8B8A8UIntNormalized, + 2, + item.Size); + + session = framePool.CreateCaptureSession(item); + + session.IsCursorCaptureEnabled = false; + session.StartCapture( ); + while ((direct3D11CaptureFrame = framePool.TryGetNextFrame()) == null) + { + } + + using var bitmap = CreateSharpDXTexture2D(direct3D11CaptureFrame.Surface); + + var mappedSubresource = new MappedSubresource(); + + Texture2DDesc stagingTextureDesc = new() + { + CPUAccessFlags = (uint)CpuAccessFlag.Read, + BindFlags = (uint)BindFlag.None, + Format = adapterForMonitor.Item2.ColorSpace.ToString().EndsWith("2020") + ? Format.FormatR16G16B16A16Float + : Format.FormatR8G8B8A8Unorm, + Width = (uint)item.Size.Width, + Height = (uint)item.Size.Height, + MiscFlags = (uint)ResourceMiscFlag.None, + MipLevels = 1, + ArraySize = 1, + SampleDesc = { Count = 1, Quality = 0 }, + Usage = Usage.Staging + }; + + if (d3dDevice->CreateTexture2D(&stagingTextureDesc, null, ref stagingTexture) != 0) + throw new Exception("Failed to create staging texture"); + + stagingTexture->QueryInterface(out stagingResource); + immediateContext->CopyResource(stagingResource, bitmap); + if (immediateContext->Map(stagingResource, 0, Map.Read, 0, &mappedSubresource) != 0) + throw new Exception("Failed to map staging texture"); + + //更新窗口的Size数据 + screenCaptureInfo.WindowInfo.Rect = new Rect(0,0,item.Size.Width, item.Size.Height); + var bytesSpan = CaptureTool.GetBytesSpan(mappedSubresource,adapterForMonitor.Item2,ref screenCaptureInfo); + + return new ScreenCaptureResult() + { + Info = screenCaptureInfo, + Bytes = bytesSpan + }; + } + finally + { + + + adapter1.Release(); + adapter1 = null; + + + if (context != null) + { + context->Release(); + context = null; + } + + if (immediateContext != null) + { + immediateContext->Release(); + immediateContext = null; + } + + if (d3dDevice != null) + { + d3dDevice->Release(); + d3dDevice = null; + } + + + stagingResource.Release(); + stagingResource = null; + + if (direct3D11CaptureFrame != null) + { + direct3D11CaptureFrame.Dispose(); + direct3D11CaptureFrame = null; + } + + if (stagingTexture != null) + { + stagingTexture->Release(); + stagingTexture = null; + } + + if (framePool != null) + { + framePool.Dispose(); + framePool = null; + } + + if (session != null) + { + + session.Dispose(); + session = null; + } + } + } +} \ No newline at end of file diff --git a/KitopiaTest/KitopiaTest.csproj b/KitopiaTest/KitopiaTest.csproj index 323ef6a..aca201a 100644 --- a/KitopiaTest/KitopiaTest.csproj +++ b/KitopiaTest/KitopiaTest.csproj @@ -9,10 +9,10 @@ - net8.0 + net9.0 - net8.0-windows10.0.17763.0 + net9.0-windows10.0.17763.0