Net中使用快速高性能的DX截图
使用DX获取桌面图像主要有如下5步:
1 获取适配器;
2获取输出;
3创建设备;
4创建复制输出;
5创建图像纹理
使用的组件,Opencv使用mat 进行分辨率转换压缩
using 指令集合
using System; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using OpenCvSharp; using SharpDX; using SharpDX.Direct3D11; using SharpDX.DXGI; using Device = SharpDX.Direct3D11.Device; using MapFlags = SharpDX.Direct3D11.MapFlags; using Size = System.Drawing.Size; using ResultCode = SharpDX.DXGI.ResultCode;
详情(注意使用DXGI只能输出RGBA像素格式,以及注意创建适配和释放适配器,对性能的要求TimeOut很重要可根据具体情况调试设置)
1 创建
/// <summary>
/// 捕获图像大小,默认为捕获源大小,建议按原始比例设置
/// </summary>
public Size CaptureSize { get; set; }
/// <summary>
/// 输出数据像素格式,默认为BGRA32
/// </summary>
public PixelFormat PixelFormat { get; set; } = PixelFormat.Bgra32;
/// <summary>
/// 捕获超时时间
/// </summary>
public int TimeOut { get; set; } = 100;
/// <summary>
/// 捕获源类型
/// </summary>
public SourceType SourceType => SourceType.Screen;
/// <summary>
/// 捕获源大小
/// </summary>
public Size SourceSize { get; }
// 设备接口表示一个虚拟适配器
private readonly Device _mDevice;
// 复制输出设备
private readonly OutputDuplication _mDeskDupl;
//图像纹理
private readonly Texture2D _desktopImageTexture;
//屏幕大小
private Size _screenSize;
private CancellationTokenSource _cancellationTokenSource;
private bool _isManualCaptureStop;
/// <summary>
/// 复制指定监视器的输出
/// </summary>
/// <param name="whichMonitor">要复制的输出设备(即监视器)。以零开始,应于主监视器.</param>
public DxgiCapture(int whichMonitor = 0) : this(0, whichMonitor) { }
/// <summary>
/// 在指定的显卡适配器上复制指定监视器的输出
/// </summary>
/// <remarks>1获取适配器;2获取输出;3创建设备;4创建复制输出; 5创建图像纹理</remarks>
/// <param name="whichGraphicsCardAdapter">显卡适配器</param>
/// <param name="whichOutputDevice">要复制的输出设备(即监视器)。以零开始,应于主监视器.</param>
public DxgiCapture(int whichGraphicsCardAdapter, int whichOutputDevice)
{
var primaryAdapter = GetPrimaryAdapter(whichGraphicsCardAdapter);
Output primaryOutput = GetOutput(primaryAdapter, whichOutputDevice);
// 获取输出对象的描述信息
var mOutputDesc = primaryOutput.Description;
_screenSize = new Size
{
Width = Math.Abs(mOutputDesc.DesktopBounds.Right - mOutputDesc.DesktopBounds.Left),
Height = Math.Abs(mOutputDesc.DesktopBounds.Bottom - mOutputDesc.DesktopBounds.Top)
};
SourceSize = _screenSize;
CaptureSize = SourceSize;
//创建Direct3D设备
_mDevice = CreateDevice(primaryAdapter);
_mDeskDupl = CreateOutputDuplication(primaryOutput, _mDevice);
// 创建共享资源
_desktopImageTexture = CreateTexture2D(_mDevice, _screenSize);
}
/// <summary>
/// 根据显卡编号获取适配器
/// </summary>
/// <remarks>获取输出设备(显卡、显示器),这里是主显卡和主显示器</remarks>
/// <param name="whichGraphicsCardAdapter"></param>
/// <returns></returns>
private Adapter1 GetPrimaryAdapter(int whichGraphicsCardAdapter)
{
try
{
var adapter = new Factory1().GetAdapter1(whichGraphicsCardAdapter);
return adapter;
}
catch (SharpDXException)
{
throw new CaptureException("找不到指定的显卡适配器");
}
}
/// <summary>
/// 根据显示适配器和输出设备获取输出
/// </summary>
/// <param name="adapter">显示适配器</param>
/// <param name="whichOutputDevice">输出设备(监视器)</param>
/// <returns></returns>
private Output GetOutput(Adapter1 adapter, int whichOutputDevice)
{
try
{
return adapter.GetOutput(whichOutputDevice);
}
catch (SharpDXException)
{
throw new CaptureException("找不到指定的输出设备");
}
}
/// <summary>
/// 根据显卡适配器(视频卡)创建设备
/// </summary>
/// <param name="adapter"></param>
/// <returns></returns>
private Device CreateDevice(Adapter1 adapter)
{
return new Device(adapter);
//return new Device(DriverType.Hardware, DeviceCreationFlags.VideoSupport);
}
/// <summary>
/// 根据输出和设备创建输出Duplication
/// </summary>
/// <param name="output">输出</param>
/// <param name="mDevice">设备</param>
/// <returns></returns>
private OutputDuplication CreateOutputDuplication(Output output, Device mDevice)
{
try
{
var mDeskDupl = output.QueryInterface<Output1>().DuplicateOutput(mDevice);
if (mDeskDupl == null)
{
throw new CaptureException(" Creates a desktop duplication interface Error");
}
return mDeskDupl;
}
catch (SharpDXException ex)
{
if (ex.ResultCode.Code == ResultCode.NotCurrentlyAvailable.Result.Code)
{
throw new CaptureException("使用桌面复制API运行的应用程序已达到最大数量,请关闭其中一个应用程序,然后重试");
}
throw new CaptureException(ex.ToString());
}
}
/// <summary>
/// 创建2D纹理
/// </summary>
/// <remarks>共享资源</remarks>
/// <param name="mDevice"></param>
/// <param name="screenSize"></param>
/// <returns></returns>
private Texture2D CreateTexture2D(Device mDevice, Size screenSize)
{
return new Texture2D(mDevice, new Texture2DDescription
{
CpuAccessFlags = CpuAccessFlags.Read,
BindFlags = BindFlags.None,
Format = Format.B8G8R8A8_UNorm,
Width = screenSize.Width,
Height = screenSize.Height,
OptionFlags = ResourceOptionFlags.None,
MipLevels = 1,
ArraySize = 1,
SampleDescription = { Count = 1, Quality = 0 },
Usage = ResourceUsage.Staging
});
}
2 获取
[HandleProcessCorruptedStateExceptions] private CaptureFrame CaptureFrames() { try { var width = _desktopImageTexture.Description.Width; var height = _desktopImageTexture.Description.Height; var data = PixelFormat switch { PixelFormat.Bgra32 => new byte[CaptureSize.Width * CaptureSize.Height * 4], PixelFormat.Bgr24 => new byte[CaptureSize.Width * CaptureSize.Height * 3], _ => throw new ArgumentOutOfRangeException(nameof(PixelFormat), PixelFormat, null) }; var result = _mDeskDupl.TryAcquireNextFrame(TimeOut, out _, out var desktopResource); if (result.Failure) return null; using var tempTexture = desktopResource?.QueryInterface<Texture2D>(); _mDevice.ImmediateContext.CopyResource(tempTexture, _desktopImageTexture); //拷贝图像纹理:GPU硬件加速的纹理复制 desktopResource?.Dispose(); var desktopSource = _mDevice.ImmediateContext.MapSubresource(_desktopImageTexture, 0, MapMode.Read, MapFlags.None); using var inputRgbaMat = new Mat(_screenSize.Height, _screenSize.Width, MatType.CV_8UC4, desktopSource.DataPointer); if (CaptureSize.Width != _screenSize.Width || CaptureSize.Height != _screenSize.Height) { var size = new OpenCvSharp.Size(CaptureSize.Width, CaptureSize.Height); Cv2.Resize(inputRgbaMat, inputRgbaMat, size, interpolation: InterpolationFlags.Linear); } if (PixelFormat == PixelFormat.Bgr24) { using var inputRgbMat = new Mat(); Cv2.CvtColor(inputRgbaMat, inputRgbMat, ColorConversionCodes.BGRA2BGR); Marshal.Copy(inputRgbMat.Data, data, 0, data.Length); } else { Marshal.Copy(inputRgbaMat.Data, data, 0, data.Length); } var captureFrame = new CaptureFrame(CaptureSize, PixelFormat, data); _mDevice.ImmediateContext.UnmapSubresource(_desktopImageTexture, 0); ReleaseFrame(_mDeskDupl); return captureFrame; } catch (AccessViolationException) { return null; } catch (Exception) { return null; } }
3 释放
/// <summary> /// 释放资源 /// </summary> public void Dispose() { _mDevice?.Dispose(); _mDeskDupl?.Dispose(); _desktopImageTexture?.Dispose(); } /// <summary> /// 释放帧 /// </summary> /// <param name="mDeskDupl"></param> private void ReleaseFrame(OutputDuplication mDeskDupl) { try { mDeskDupl.ReleaseFrame(); } catch (SharpDXException ex) { if (ex.ResultCode.Failure) throw new CaptureException("释放帧失败"); throw; } }
4 创建回调获取方式
/// <summary> /// 开始捕获 /// </summary> public void StartCapture(bool startMonitor = true) { _isManualCaptureStop = false; _cancellationTokenSource = new CancellationTokenSource(); if (!startMonitor) return; Task.Run(() => { try { while (!_cancellationTokenSource.IsCancellationRequested) { var captureFrame = CaptureFrames(); if (captureFrame != null) { FrameArrived?.Invoke(this, captureFrame); } } } catch (Exception) { //ignore } }, _cancellationTokenSource.Token); } /// <summary> /// 停止捕获 /// </summary> public void StopCapture() { _cancellationTokenSource?.Cancel(); Dispose(); _isManualCaptureStop = true; } /// <summary> /// 获取下一帧图像 /// </summary> /// <param name="captureFrame"></param> /// <returns></returns> public bool TryGetNextFrame(out CaptureFrame captureFrame) { captureFrame = null; if (_isManualCaptureStop) return false; _cancellationTokenSource?.Cancel(); captureFrame = CaptureFrames(); return captureFrame != null; } /// <summary> /// 新帧到达事件 /// </summary> public event EventHandler<CaptureFrame> FrameArrived;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)