From 24b67a10c2f891be59d1a354bd4d0087747165f4 Mon Sep 17 00:00:00 2001 From: MakesYT <42534870+MakesYT@users.noreply.github.com> Date: Fri, 31 May 2024 23:32:13 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=BB=E9=99=A4=20ScreenCapture.NET=E5=92=8C?= =?UTF-8?q?DesktopNotifications=20=E9=87=8D=E6=9E=84=20=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 6 - Core/Core.csproj | 3 +- Core/SDKs/Tools/ScreenCapture.cs | 294 ++++++++++++++++++++----------- DesktopNotifications | 1 - Kitopia.sln | 44 ----- ScreenCapture.NET | 1 - 6 files changed, 194 insertions(+), 155 deletions(-) delete mode 160000 DesktopNotifications delete mode 160000 ScreenCapture.NET diff --git a/.gitmodules b/.gitmodules index f7977e2..9f14dbd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,12 +10,6 @@ [submodule "NodifyM.Avalonia"] path = NodifyM.Avalonia url = https://github.com/MakesYT/NodifyM.Avalonia/ -[submodule "DesktopNotifications"] - path = DesktopNotifications - url = https://github.com/pr8x/DesktopNotifications -[submodule "ScreenCapture.NET"] - path = ScreenCapture.NET - url = https://github.com/MakesYT/ScreenCapture.NET.git [submodule "PinyinM.NET"] path = PinyinM.NET url = https://github.com/MakesYT/Pinyin.NET diff --git a/Core/Core.csproj b/Core/Core.csproj index 6cad100..33852a3 100644 --- a/Core/Core.csproj +++ b/Core/Core.csproj @@ -42,6 +42,7 @@ + @@ -55,8 +56,6 @@ - - diff --git a/Core/SDKs/Tools/ScreenCapture.cs b/Core/SDKs/Tools/ScreenCapture.cs index fadcd16..8277ce2 100644 --- a/Core/SDKs/Tools/ScreenCapture.cs +++ b/Core/SDKs/Tools/ScreenCapture.cs @@ -3,9 +3,13 @@ using Avalonia.Media.Imaging; using Avalonia.Platform; using Core.SDKs.Services; +using log4net; using Microsoft.Extensions.DependencyInjection; using PluginCore; -using ScreenCapture.NET; +using Silk.NET.Core.Contexts; +using Silk.NET.Core.Native; +using Silk.NET.Direct3D11; +using Silk.NET.DXGI; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Gif; @@ -25,6 +29,8 @@ namespace Core.SDKs.Tools; public class ScreenCapture : IScreenCapture { private static readonly Lazy Lazy = new(CreateDefaultInstance); + public static Configuration Configuration => Lazy.Value; + private static readonly ILog log = LogManager.GetLogger(nameof(ScreenCapture)); private static Configuration CreateDefaultInstance() { @@ -43,136 +49,222 @@ private static Configuration CreateDefaultInstance() }; } + public class DisposableTool(Action busySetter) : IDisposable + { + public void Dispose() => busySetter.Invoke(); + } + public (Queue, Queue) CaptureAllScreen() { Queue bitmaps = new(); Queue mosaics = new(); - using var screenCaptureService = new DX11ScreenCaptureService(); - var graphicsCards = screenCaptureService.GetGraphicsCards(); - - var enumerable = screenCaptureService.GetDisplays(graphicsCards.First()); - foreach (Display display in enumerable) + unsafe { - using var screenCapture = screenCaptureService.GetScreenCapture(display); - var captureZone = screenCapture.RegisterCaptureZone(0, 0, display.Width, display.Height); - - while (!screenCapture.CaptureScreen()) ; - using var loadPixelData = Image.LoadPixelData(Lazy.Value, - captureZone.RawBuffer, - captureZone.Width, captureZone.Height); - var clone = loadPixelData.Clone(); - using (clone) + DXGI dxgi = new DXGI(new DefaultNativeContext("dxgi")); + ComPtr factory = null; + IDXGIAdapter1* adapter1 = null; + ID3D11Device* device = null; + ID3D11DeviceContext* context = null; + ID3D11DeviceContext* immediateContext = null; + D3D11 d3D11 = new D3D11(new DefaultNativeContext("d3d11")); + try { - clone.Mutate(x => x.BoxBlur(10)); - if (!clone.DangerousTryGetSinglePixelMemory(out Memory memory1)) + if (dxgi.CreateDXGIFactory1(out factory) != 0) { - throw new Exception( - "This can only happen with multi-GB images or when PreferContiguousImageBuffers is not set to true."); + throw new Exception("Failed to create DXGI factory"); } - using (MemoryHandle pinHandle = memory1.Pin()) + if (factory.EnumAdapters1(0, ref adapter1) != 0) { - unsafe + throw new Exception("Failed to create DXGI adapter"); + } + + D3DFeatureLevel featureLevel = D3DFeatureLevel.Level101; + D3DFeatureLevel[] featureLevels = + [ + D3DFeatureLevel.Level111, D3DFeatureLevel.Level110, D3DFeatureLevel.Level101, + D3DFeatureLevel.Level100 + ]; + + fixed (D3DFeatureLevel* pFeatureLevels = &featureLevels[0]) + { + if (d3D11.CreateDevice((IDXGIAdapter*)adapter1, D3DDriverType.Unknown, IntPtr.Zero, + (uint)CreateDeviceFlag.None, pFeatureLevels, (uint)featureLevels.Length, D3D11.SdkVersion, + ref device, + &featureLevel, ref context) != 0) { - var bitmap1 = new Bitmap(PixelFormat.Bgra8888, AlphaFormat.Unpremul, (IntPtr)pinHandle.Pointer, - new PixelSize(captureZone.Width, captureZone.Height), new Vector(96, 96), - (captureZone.Height * PixelFormat.Bgra8888.BitsPerPixel + 7) / 8); - mosaics.Enqueue(bitmap1); + throw new Exception("Failed to create D3D11 device"); } } - } - if (!loadPixelData.DangerousTryGetSinglePixelMemory(out Memory memory)) - { - throw new Exception( - "This can only happen with multi-GB images or when PreferContiguousImageBuffers is not set to true."); - } + device->GetImmediateContext(ref immediateContext); - using (MemoryHandle pinHandle = memory.Pin()) - { - unsafe + uint i = 0; + IDXGIOutput* output = null; + while (adapter1->EnumOutputs(i, ref output) == 0) { - var bitmap1 = new Bitmap(PixelFormat.Bgra8888, AlphaFormat.Unpremul, (IntPtr)pinHandle.Pointer, - new PixelSize(captureZone.Width, captureZone.Height), new Vector(96, 96), - (captureZone.Height * PixelFormat.Bgra8888.BitsPerPixel + 7) / 8); - bitmaps.Enqueue(bitmap1); - } - } - } + i++; + IDXGIOutputDuplication* outputDuplication = null; + IDXGIResource* desktopResource = null; + ID3D11Texture2D* stagingTexture = null; + ComPtr output5 = null; + ComPtr desktopTexture = null; + ComPtr stagingResource = null; + try + { + OutputDesc desc = new OutputDesc(null); + if (output->GetDesc(ref desc) != 0) + { + throw new Exception("Failed to get output description"); + } - return (bitmaps, mosaics); - } + if (output->QueryInterface(out output5) != 0) + { + throw new Exception("Failed to get IDXGIOutput5"); + } - public (Bitmap?, Bitmap?)? CaptureScreen(ScreenCaptureInfo screenCaptureInfo, bool withMosaic = false) - { - using var screenCaptureService = new DX11ScreenCaptureService(); - var graphicsCards = screenCaptureService.GetGraphicsCards(); - var enumerable = screenCaptureService.GetDisplays(graphicsCards.First()); - if (screenCaptureInfo.Index + 1 > enumerable.Count()) - { - return (null, null); - } + if (output5.DuplicateOutput((IUnknown*)device, ref outputDuplication) != 0) + { + throw new Exception("Failed to get output duplication"); + } - var display = enumerable.ElementAtOrDefault(screenCaptureInfo.Index); - var x = System.Math.Max(screenCaptureInfo.X, 0); - var y = System.Math.Max(screenCaptureInfo.Y, 0); - var width = System.Math.Max(screenCaptureInfo.Width, display.Width); - var height = System.Math.Max(screenCaptureInfo.Height, display.Height); - - using var screenCapture = screenCaptureService.GetScreenCapture(display); - var captureZone = screenCapture.RegisterCaptureZone(x, y, width, height); - - while (!screenCapture.CaptureScreen()) ; - using var loadPixelData = Image.LoadPixelData(Lazy.Value, - captureZone.RawBuffer, - captureZone.Width, captureZone.Height); - Bitmap? mosaic = null; - if (withMosaic) - { - var clone = loadPixelData.Clone(); - using (clone) - { - clone.Mutate(x => x.BoxBlur(10)); - if (!clone.DangerousTryGetSinglePixelMemory(out Memory memory1)) - { - throw new Exception( - "This can only happen with multi-GB images or when PreferContiguousImageBuffers is not set to true."); - } + OutduplFrameInfo outduplFrameInfo = new OutduplFrameInfo(); + Thread.Sleep(10); + if (outputDuplication->AcquireNextFrame(2000, &outduplFrameInfo, &desktopResource) != 0 || + outduplFrameInfo.LastPresentTime == 0) + { + throw new Exception("Failed to acquire next frame"); + } - using (MemoryHandle pinHandle = memory1.Pin()) - { - unsafe + if (desktopResource->QueryInterface(out desktopTexture) != 0) + { + throw new Exception("Failed to get desktop texture"); + } + + Texture2DDesc stagingTextureDesc = new() + { + CPUAccessFlags = (uint)CpuAccessFlag.Read, + BindFlags = (uint)(BindFlag.None), + Format = Format.FormatB8G8R8A8Unorm, + Width = (uint)desc.DesktopCoordinates.Size.X, + Height = (uint)desc.DesktopCoordinates.Size.Y, + MiscFlags = (uint)ResourceMiscFlag.None, + MipLevels = 1, + ArraySize = 1, + SampleDesc = { Count = 1, Quality = 0 }, + Usage = Usage.Staging + }; + + if (device->CreateTexture2D(&stagingTextureDesc, null, ref stagingTexture) != 0) + { + throw new Exception("Failed to create staging texture"); + } + + stagingTexture->QueryInterface(out stagingResource); + immediateContext->CopyResource(stagingResource, desktopTexture); + + MappedSubresource mappedSubresource = new MappedSubresource(); + if (immediateContext->Map(stagingResource, 0, Map.Read, 0, &mappedSubresource) != 0) + { + throw new Exception("Failed to map staging texture"); + } + + var span = new ReadOnlySpan(mappedSubresource.PData, + (int)mappedSubresource.DepthPitch); + immediateContext->Unmap(stagingResource, 0); + outputDuplication->ReleaseFrame(); + var loadPixelData = Image.LoadPixelData(CreateDefaultInstance(), span, + desc.DesktopCoordinates.Size.X, desc.DesktopCoordinates.Size.Y); + + var clone = loadPixelData.Clone(); + using (clone) + { + clone.Mutate(x => x.BoxBlur(10)); + if (!clone.DangerousTryGetSinglePixelMemory(out Memory memory1)) + { + throw new Exception( + "This can only happen with multi-GB images or when PreferContiguousImageBuffers is not set to true."); + } + + using (MemoryHandle pinHandle = memory1.Pin()) + { + var bitmap1 = new Bitmap(PixelFormat.Bgra8888, AlphaFormat.Unpremul, + (IntPtr)pinHandle.Pointer, + new PixelSize(desc.DesktopCoordinates.Size.X, desc.DesktopCoordinates.Size.Y), + new Vector(96, 96), + (desc.DesktopCoordinates.Size.Y * PixelFormat.Bgra8888.BitsPerPixel + 7) / 8); + mosaics.Enqueue(bitmap1); + } + } + + if (!loadPixelData.DangerousTryGetSinglePixelMemory(out Memory memory)) + { + throw new Exception( + "This can only happen with multi-GB images or when PreferContiguousImageBuffers is not set to true."); + } + + using (MemoryHandle pinHandle = memory.Pin()) + { + var bitmap1 = new Bitmap(PixelFormat.Bgra8888, AlphaFormat.Unpremul, + (IntPtr)pinHandle.Pointer, + new PixelSize(desc.DesktopCoordinates.Size.X, desc.DesktopCoordinates.Size.Y), + new Vector(96, 96), + (desc.DesktopCoordinates.Size.Y * PixelFormat.Bgra8888.BitsPerPixel + 7) / 8); + bitmaps.Enqueue(bitmap1); + } + + loadPixelData.Dispose(); + } + catch (Exception e) { - var bitmap1 = new Bitmap(PixelFormat.Bgra8888, AlphaFormat.Unpremul, (IntPtr)pinHandle.Pointer, - new PixelSize(captureZone.Width, captureZone.Height), new Vector(96, 96), - (captureZone.Height * PixelFormat.Bgra8888.BitsPerPixel + 7) / 8); - mosaic = bitmap1; + log.Error(e); + } + finally + { + output->Release(); + outputDuplication->Release(); + desktopResource->Release(); + stagingTexture->Release(); + output5.Release(); + desktopTexture.Release(); + stagingResource.Release(); + output = null; + outputDuplication = null; + desktopResource = null; + stagingTexture = null; + output5 = null; + desktopTexture = null; + stagingResource = null; } } } - } - - if (!loadPixelData.DangerousTryGetSinglePixelMemory(out Memory memory)) - { - throw new Exception( - "This can only happen with multi-GB images or when PreferContiguousImageBuffers is not set to true."); - } - - Bitmap? bitmap = null; - using (MemoryHandle pinHandle = memory.Pin()) - { - unsafe + finally { - var bitmap1 = new Bitmap(PixelFormat.Bgra8888, AlphaFormat.Unpremul, (IntPtr)pinHandle.Pointer, - new PixelSize(captureZone.Width, captureZone.Height), new Vector(96, 96), - (captureZone.Height * PixelFormat.Bgra8888.BitsPerPixel + 7) / 8); - bitmap = bitmap1; + dxgi.Dispose(); + d3D11.Dispose(); + factory.Dispose(); + adapter1->Release(); + device->Release(); + context->Release(); + immediateContext->Release(); + dxgi = null; + d3D11 = null; + factory = null; + adapter1 = null; + device = null; + context = null; + immediateContext = null; } } - return (bitmap, mosaic); + return (bitmaps, mosaics); + } + + public (Bitmap?, Bitmap?)? CaptureScreen(ScreenCaptureInfo screenCaptureInfo, bool withMosaic = false) + { + return (null, null); } public ScreenCaptureInfo GetScreenCaptureInfoByUserManual() diff --git a/DesktopNotifications b/DesktopNotifications deleted file mode 160000 index 8721091..0000000 --- a/DesktopNotifications +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 87210913337abfa25b790e75a9f2560e7c438a42 diff --git a/Kitopia.sln b/Kitopia.sln index 3627270..5841b97 100644 --- a/Kitopia.sln +++ b/Kitopia.sln @@ -29,10 +29,6 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "kitopia", "build\kitopia.csproj", "{4698A8B2-97C4-4B97-BF66-C12F9EAFC964}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenCapture.NET.DX11", "ScreenCapture.NET\ScreenCapture.NET.DX11\ScreenCapture.NET.DX11.csproj", "{0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenCapture.NET", "ScreenCapture.NET\ScreenCapture.NET\ScreenCapture.NET.csproj", "{3078161E-00CA-4427-8A9A-D38B6DA64E34}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KitopiaTest", "KitopiaTest\KitopiaTest.csproj", "{49C48ABE-0FD1-4141-94F1-347D4E5E09E2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KitopiaBenchmark", "KitopiaBenchmark\KitopiaBenchmark.csproj", "{E175C03A-AF27-43DB-8BE5-669339A7B5D7}" @@ -255,46 +251,6 @@ Global {88621E02-A1A5-4896-A43C-B536803C1AFE}.Release|x64.Build.0 = Release|Any CPU {88621E02-A1A5-4896-A43C-B536803C1AFE}.Release|x86.ActiveCfg = Release|Any CPU {88621E02-A1A5-4896-A43C-B536803C1AFE}.Release|x86.Build.0 = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|ARM.ActiveCfg = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|ARM.Build.0 = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|arm64.ActiveCfg = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|arm64.Build.0 = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|x64.ActiveCfg = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|x64.Build.0 = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|x86.ActiveCfg = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Debug|x86.Build.0 = Debug|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|Any CPU.Build.0 = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|ARM.ActiveCfg = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|ARM.Build.0 = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|arm64.ActiveCfg = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|arm64.Build.0 = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|x64.ActiveCfg = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|x64.Build.0 = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|x86.ActiveCfg = Release|Any CPU - {0357DDD4-6EC0-475F-BFB3-B6C5E4CB649A}.Release|x86.Build.0 = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|ARM.ActiveCfg = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|ARM.Build.0 = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|arm64.ActiveCfg = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|arm64.Build.0 = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|x64.ActiveCfg = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|x64.Build.0 = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|x86.ActiveCfg = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Debug|x86.Build.0 = Debug|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|Any CPU.Build.0 = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|ARM.ActiveCfg = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|ARM.Build.0 = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|arm64.ActiveCfg = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|arm64.Build.0 = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|x64.ActiveCfg = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|x64.Build.0 = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|x86.ActiveCfg = Release|Any CPU - {3078161E-00CA-4427-8A9A-D38B6DA64E34}.Release|x86.Build.0 = Release|Any CPU {49C48ABE-0FD1-4141-94F1-347D4E5E09E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {49C48ABE-0FD1-4141-94F1-347D4E5E09E2}.Debug|Any CPU.Build.0 = Debug|Any CPU {49C48ABE-0FD1-4141-94F1-347D4E5E09E2}.Debug|ARM.ActiveCfg = Debug|Any CPU diff --git a/ScreenCapture.NET b/ScreenCapture.NET deleted file mode 160000 index 6ab24f1..0000000 --- a/ScreenCapture.NET +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6ab24f1b3058fd5cc8c373a2aad728f7cadc4ecc