Skip to content

Commit

Permalink
优化 截图性能
Browse files Browse the repository at this point in the history
  • Loading branch information
MakesYT committed Feb 26, 2025
1 parent 4cb4524 commit 6877ba0
Show file tree
Hide file tree
Showing 12 changed files with 754 additions and 69 deletions.
49 changes: 1 addition & 48 deletions Core.Window/ClipboardWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Gdi32.BITMAPINFO>()];
var img = new byte[result.Length - Marshal.SizeOf<Gdi32.BITMAPINFO>() + 4];
Array.Copy(result, 40, img, 0, img.Length);
Array.Copy(result, 0, imgInfo, 0, imgInfo.Length);
var info = BytesToStructure<Gdi32.BITMAPINFO>(imgInfo);
var configuration = Configuration.Default;
configuration.PreferContiguousImageBuffers = true;

var image = Image.LoadPixelData<Rgba32>(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)
Expand Down
5 changes: 3 additions & 2 deletions Core.Window/ScreenCapture/CaptureTool.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using PluginCore;
using System.Buffers;
using PluginCore;
using Silk.NET.Direct3D11;
using Silk.NET.DXGI;

Expand Down Expand Up @@ -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<byte>.Shared.Rent(regionWidth * regionHeight * 4);
if (!outputDesc.ColorSpace.ToString().EndsWith("2020"))
{
var span = new ReadOnlySpan<uint>(mappedSubresource.PData,
Expand Down
5 changes: 3 additions & 2 deletions Core.Window/ScreenCapture/ScreenCaptureByDx11.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections;
using System.Buffers;
using System.Collections;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -421,7 +422,7 @@ public Stack<ScreenCaptureResult> CaptureAllScreenBitmap()
}
}

captureAllScreenInfo.Bytes = null;
ArrayPool<byte>.Shared.Return(captureAllScreenInfo.Bytes);
captureAllScreenInfo.Source = writeableBitmap;
screenCaptureResults.Push(captureAllScreenInfo);
}
Expand Down
5 changes: 3 additions & 2 deletions Core.Window/ScreenCapture/ScreenCaptureByWGC.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -260,7 +261,7 @@ public ScreenCaptureResult CaptureScreenBitmap(ScreenCaptureResult captureAllScr
}
}

captureAllScreenInfo.Bytes = null;
ArrayPool<byte>.Shared.Return(captureAllScreenInfo.Bytes);
captureAllScreenInfo.Source = writeableBitmap;
return captureAllScreenInfo;
}
Expand Down
12 changes: 6 additions & 6 deletions KitopiaAvalonia/Services/ScreenCaptureWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ public void CaptureScreen()
var results = ServiceManager.Services.GetService<IScreenCaptureManager>()!.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();
}

Expand All @@ -33,8 +33,8 @@ public void RequestUserSelectScreenInfo(Action<ScreenCaptureInfo> action)
var results = ServiceManager.Services.GetService<IScreenCaptureManager>()!.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();
}
Expand All @@ -50,8 +50,8 @@ public void RequestUserSelectScreenBytes(Action<ScreenCaptureResult> 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)
Expand Down
12 changes: 8 additions & 4 deletions KitopiaAvalonia/Windows/ScreenCaptureWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
Expand Down Expand Up @@ -51,12 +52,13 @@ public partial class ScreenCaptureWindow : Window
private bool Finish = false;
private List<WindowInfo> _windowInfos;
private WindowInfo _currentWindowInfo;
public ScreenCaptureWindow(ScreenCaptureInfo screenCaptureInfo)
public ScreenCaptureWindow(ScreenCaptureResult screenCaptureResult)
{
InitializeComponent();
_windowInfos = ServiceManager.Services.GetService<IScreenCaptureManager>()!.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);
Expand Down Expand Up @@ -943,7 +945,7 @@ private void FinnishCapture()
bufferSize,
(((int)cropW * PixelFormat.Rgba8888.BitsPerPixel + 31) & ~31) >> 3
);
var ys = new byte[bufferSize];
var ys =ArrayPool<byte>.Shared.Rent(bufferSize);
Marshal.Copy(ptr, ys, 0, bufferSize);
Marshal.FreeHGlobal(ptr);
if (selectBytesMode)
Expand All @@ -965,6 +967,7 @@ private void FinnishCapture()
});
}).ContinueWith((e) =>
{
ArrayPool<byte>.Shared.Return(ys);
GC.Collect(2,GCCollectionMode.Optimized);
});

Expand All @@ -987,6 +990,7 @@ private void FinnishCapture()
Bytes = ys
}).ContinueWith((e) =>
{
ArrayPool<byte>.Shared.Return(ys);
GC.Collect(2,GCCollectionMode.Optimized);
});
}
Expand Down
113 changes: 113 additions & 0 deletions KitopiaBenchmark/CaptureTool.cs
Original file line number Diff line number Diff line change
@@ -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<uint>(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<Half>(mappedSubresource.PData,
(int)mappedSubresource.DepthPitch / 2).ToArray();
// ReadOnlyMemory<Half> readOnlyMemory = new ReadOnlyMemory<Half>(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;
}
}
13 changes: 11 additions & 2 deletions KitopiaBenchmark/KitopiaBenchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@

<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' != 'Windows_NT' ">
<TargetFrameworks>net8.0</TargetFrameworks>
<TargetFrameworks>net9.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
<TargetFrameworks>net8.0-windows10.0.17763.0</TargetFrameworks>
<TargetFrameworks>net9.0-windows10.0.19041.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<WindowsPackageType>None</WindowsPackageType>
<EnableMsixTooling>true</EnableMsixTooling>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.12"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core.Window\Core.Window.csproj" />
<ProjectReference Include="..\Core\Core.csproj" />
<ProjectReference Include="..\KitopiaAvalonia\KitopiaAvalonia.csproj" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion KitopiaBenchmark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ internal class Program
{
private static void Main(string[] args)
{
BenchmarkRunner.Run<NameSolver>();
BenchmarkRunner.Run<ScreenCapture>();
}
}
56 changes: 56 additions & 0 deletions KitopiaBenchmark/ScreenCapture.cs
Original file line number Diff line number Diff line change
@@ -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<byte>.Shared.Return(result.Bytes);
}
}
[Benchmark]
public void WithoutPool()
{
var captureAllScreenBytes = _screenCaptureByWgcWithoutPool.CaptureAllScreenBytes();
while (captureAllScreenBytes.TryPop(out var result))
{
result.Bytes = null;
// ArrayPool<byte>.Shared.Return(result.Bytes);
}
}
[Benchmark]
public void PoolButDontReturn()
{
var captureAllScreenBytes = _screenCaptureByWgc.CaptureAllScreenBytes();
while (captureAllScreenBytes.TryPop(out var result))
{
// ArrayPool<byte>.Shared.Return(result.Bytes);
}
}
[Benchmark]
public void Pool()
{
var captureAllScreenBytes = _screenCaptureByWgc.CaptureAllScreenBytes();
while (captureAllScreenBytes.TryPop(out var result))
{
ArrayPool<byte>.Shared.Return(result.Bytes);
}
}
}
Loading

0 comments on commit 6877ba0

Please sign in to comment.