Skip to content

Commit

Permalink
移除 ScreenCapture.NET和DesktopNotifications
Browse files Browse the repository at this point in the history
重构 截图实现
  • Loading branch information
MakesYT committed May 31, 2024
1 parent 8d9d808 commit 24b67a1
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 155 deletions.
6 changes: 0 additions & 6 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 1 addition & 2 deletions Core/Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0"/>
<PackageReference Include="SharpHook" Version="5.3.5"/>
<PackageReference Include="SharpHook.Reactive" Version="5.3.5"/>
<PackageReference Include="Silk.NET.Direct3D11" Version="2.21.0"/>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4"/>
<PackageReference Include="System.Drawing.Common" Version="8.0.1"/>
<PackageReference Include="System.Management" Version="8.0.0"/>
Expand All @@ -55,8 +56,6 @@

<ItemGroup>
<ProjectReference Include="..\PinyinM.NET\Pinyin.NET\Pinyin.NET.csproj" />
<ProjectReference Include="..\ScreenCapture.NET\ScreenCapture.NET.DX11\ScreenCapture.NET.DX11.csproj"/>
<ProjectReference Include="..\ScreenCapture.NET\ScreenCapture.NET\ScreenCapture.NET.csproj"/>
<ProjectReference Include="..\PluginCore\PluginCore.csproj"/>
</ItemGroup>

Expand Down
294 changes: 193 additions & 101 deletions Core/SDKs/Tools/ScreenCapture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,6 +29,8 @@ namespace Core.SDKs.Tools;
public class ScreenCapture : IScreenCapture
{
private static readonly Lazy<Configuration> Lazy = new(CreateDefaultInstance);
public static Configuration Configuration => Lazy.Value;
private static readonly ILog log = LogManager.GetLogger(nameof(ScreenCapture));

private static Configuration CreateDefaultInstance()
{
Expand All @@ -43,136 +49,222 @@ private static Configuration CreateDefaultInstance()
};
}

public class DisposableTool(Action busySetter) : IDisposable
{
public void Dispose() => busySetter.Invoke();
}

public (Queue<Bitmap>, Queue<Bitmap>) CaptureAllScreen()
{
Queue<Bitmap> bitmaps = new();
Queue<Bitmap> 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<Bgra32>(Lazy.Value,
captureZone.RawBuffer,
captureZone.Width, captureZone.Height);
var clone = loadPixelData.Clone();
using (clone)
DXGI dxgi = new DXGI(new DefaultNativeContext("dxgi"));
ComPtr<IDXGIFactory1> 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<Bgra32> memory1))
if (dxgi.CreateDXGIFactory1<IDXGIFactory1>(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<Bgra32> 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<IDXGIOutput5> output5 = null;
ComPtr<ID3D11Resource> desktopTexture = null;
ComPtr<ID3D11Resource> 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<IDXGIOutput5>(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<Bgra32>(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<Bgra32> 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<ID3D11Resource>(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<ID3D11Resource>(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<byte>(mappedSubresource.PData,
(int)mappedSubresource.DepthPitch);
immediateContext->Unmap(stagingResource, 0);
outputDuplication->ReleaseFrame();
var loadPixelData = Image.LoadPixelData<Bgra32>(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<Bgra32> 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<Bgra32> 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<Bgra32> 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()
Expand Down
1 change: 0 additions & 1 deletion DesktopNotifications
Submodule DesktopNotifications deleted from 872109
Loading

0 comments on commit 24b67a1

Please sign in to comment.