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