diff --git a/ScreenCapture.NET/DirectX/DX11ScreenCapture.cs b/ScreenCapture.NET/DirectX/DX11ScreenCapture.cs
index 77d0b2d..7afc9fb 100644
--- a/ScreenCapture.NET/DirectX/DX11ScreenCapture.cs
+++ b/ScreenCapture.NET/DirectX/DX11ScreenCapture.cs
@@ -11,420 +11,419 @@ using Vortice.Mathematics;
using MapFlags = Vortice.Direct3D11.MapFlags;
using ResultCode = Vortice.DXGI.ResultCode;
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+/// Represents a ScreenCapture using DirectX 11 desktop duplicaton.
+/// https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api
+///
+// ReSharper disable once InconsistentNaming
+public sealed class DX11ScreenCapture : IScreenCapture
{
- ///
- /// Represents a ScreenCapture using DirectX 11 desktop duplicaton.
- /// https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api
- ///
- // ReSharper disable once InconsistentNaming
- public sealed class DX11ScreenCapture : IScreenCapture
+ #region Constants
+
+ private static readonly FeatureLevel[] FEATURE_LEVELS =
{
- #region Constants
+ FeatureLevel.Level_11_1,
+ FeatureLevel.Level_11_0,
+ FeatureLevel.Level_10_1,
+ FeatureLevel.Level_10_0
+ };
- private static readonly FeatureLevel[] FEATURE_LEVELS =
+ private const int BPP = 4;
+
+ #endregion
+
+ #region Properties & Fields
+
+ private readonly object _captureLock = new();
+
+ private readonly bool _useNewDuplicationAdapter;
+ private int _indexCounter = 0;
+
+ ///
+ public Display Display { get; }
+
+ ///
+ /// Gets or sets the timeout in ms used for screen-capturing. (default 1000ms)
+ /// This is used in https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutputduplication-acquirenextframe
+ ///
+ // ReSharper disable once MemberCanBePrivate.Global
+ public int Timeout { get; set; } = 1000;
+
+ private readonly IDXGIFactory1 _factory;
+
+ private IDXGIOutput? _output;
+ private IDXGIOutputDuplication? _duplicatedOutput;
+ private ID3D11Device? _device;
+ private ID3D11DeviceContext? _context;
+ private ID3D11Texture2D? _captureTexture;
+
+ private readonly Dictionary _captureZones = new();
+
+ #endregion
+
+ #region Events
+
+ ///
+ public event EventHandler? Updated;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Note that setting useNewDuplicationAdapter to true requires to call DPIAwareness.Initalize(); and prevents the capture from running in a WPF-thread.
+ ///
+ /// The used to create underlying objects.
+ /// The to duplicate.
+ /// Indicates if the DuplicateOutput1 interface should be used instead of the older DuplicateOutput. Currently there's no real use in setting this to true.
+ public DX11ScreenCapture(IDXGIFactory1 factory, Display display, bool useNewDuplicationAdapter = false)
+ {
+ this._factory = factory;
+ this.Display = display;
+ this._useNewDuplicationAdapter = useNewDuplicationAdapter;
+
+ Restart();
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public bool CaptureScreen()
+ {
+ bool result = false;
+ lock (_captureLock)
{
- FeatureLevel.Level_11_1,
- FeatureLevel.Level_11_0,
- FeatureLevel.Level_10_1,
- FeatureLevel.Level_10_0
- };
-
- private const int BPP = 4;
-
- #endregion
-
- #region Properties & Fields
-
- private readonly object _captureLock = new();
-
- private readonly bool _useNewDuplicationAdapter;
- private int _indexCounter = 0;
-
- ///
- public Display Display { get; }
-
- ///
- /// Gets or sets the timeout in ms used for screen-capturing. (default 1000ms)
- /// This is used in https://docs.microsoft.com/en-us/windows/win32/api/dxgi1_2/nf-dxgi1_2-idxgioutputduplication-acquirenextframe
- ///
- // ReSharper disable once MemberCanBePrivate.Global
- public int Timeout { get; set; } = 1000;
-
- private readonly IDXGIFactory1 _factory;
-
- private IDXGIOutput? _output;
- private IDXGIOutputDuplication? _duplicatedOutput;
- private ID3D11Device? _device;
- private ID3D11DeviceContext? _context;
- private ID3D11Texture2D? _captureTexture;
-
- private readonly Dictionary _captureZones = new();
-
- #endregion
-
- #region Events
-
- ///
- public event EventHandler? Updated;
-
- #endregion
-
- #region Constructors
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// Note that setting useNewDuplicationAdapter to true requires to call DPIAwareness.Initalize(); and prevents the capture from running in a WPF-thread.
- ///
- /// The used to create underlying objects.
- /// The to duplicate.
- /// Indicates if the DuplicateOutput1 interface should be used instead of the older DuplicateOutput. Currently there's no real use in setting this to true.
- public DX11ScreenCapture(IDXGIFactory1 factory, Display display, bool useNewDuplicationAdapter = false)
- {
- this._factory = factory;
- this.Display = display;
- this._useNewDuplicationAdapter = useNewDuplicationAdapter;
-
- Restart();
- }
-
- #endregion
-
- #region Methods
-
- ///
- public bool CaptureScreen()
- {
- bool result = false;
- lock (_captureLock)
+ if ((_context == null) || (_duplicatedOutput == null) || (_captureTexture == null))
{
- if ((_context == null) || (_duplicatedOutput == null) || (_captureTexture == null))
- {
- Restart();
- return false;
- }
-
- try
- {
- IDXGIResource? screenResource = null;
- try
- {
- _duplicatedOutput.AcquireNextFrame(Timeout, out OutduplFrameInfo duplicateFrameInformation, out screenResource);
- if ((screenResource == null) || (duplicateFrameInformation.LastPresentTime == 0)) return false;
-
- using ID3D11Texture2D screenTexture = screenResource.QueryInterface();
- _context.CopySubresourceRegion(_captureTexture, 0, 0, 0, 0, screenTexture, 0);
- }
- finally
- {
- try
- {
- screenResource?.Dispose();
- _duplicatedOutput?.ReleaseFrame();
- }
- catch { /**/ }
- }
-
- result = true;
- }
- catch (SharpGenException dxException)
- {
- if ((dxException.ResultCode == ResultCode.AccessLost)
- || (dxException.ResultCode == ResultCode.AccessDenied)
- || (dxException.ResultCode == ResultCode.InvalidCall))
- {
- try
- {
- Restart();
- }
- catch { Thread.Sleep(100); }
- }
- }
- catch { /**/ }
-
- try
- {
- UpdateZones();
- }
- catch { /**/ }
-
- try
- {
- Updated?.Invoke(this, new ScreenCaptureUpdatedEventArgs(result));
- }
- catch { /**/ }
-
- return result;
- }
- }
-
- private void UpdateZones()
- {
- if (_context == null) return;
-
- lock (_captureZones)
- {
- foreach ((CaptureZone captureZone, (ID3D11Texture2D stagingTexture, ID3D11Texture2D? scalingTexture, ID3D11ShaderResourceView? scalingTextureView)) in _captureZones.Where(z => z.Key.AutoUpdate || z.Key.IsUpdateRequested))
- {
- if (scalingTexture != null)
- {
- _context.CopySubresourceRegion(scalingTexture, 0, 0, 0, 0, _captureTexture, 0,
- new Box(captureZone.X, captureZone.Y, 0,
- captureZone.X + captureZone.UnscaledWidth,
- captureZone.Y + captureZone.UnscaledHeight, 1));
- _context.GenerateMips(scalingTextureView);
- _context.CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, scalingTexture, captureZone.DownscaleLevel);
- }
- else
- _context.CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, _captureTexture, 0,
- new Box(captureZone.X, captureZone.Y, 0,
- captureZone.X + captureZone.UnscaledWidth,
- captureZone.Y + captureZone.UnscaledHeight, 1));
-
- MappedSubresource mapSource = _context.Map(stagingTexture, 0, MapMode.Read, MapFlags.None);
- IntPtr sourcePtr = mapSource.DataPointer;
- lock (captureZone.Buffer)
- {
- for (int y = 0; y < captureZone.Height; y++)
- {
- Marshal.Copy(sourcePtr, captureZone.Buffer, y * captureZone.Stride, captureZone.Stride);
- sourcePtr += mapSource.RowPitch;
- }
- }
-
- _context.Unmap(stagingTexture, 0);
- captureZone.SetUpdated();
- }
- }
- }
-
- ///
- public CaptureZone RegisterCaptureZone(int x, int y, int width, int height, int downscaleLevel = 0)
- {
- ValidateCaptureZoneAndThrow(x, y, width, height);
-
- int unscaledWidth = width;
- int unscaledHeight = height;
- (width, height) = CalculateScaledSize(unscaledWidth, unscaledHeight, downscaleLevel);
-
- byte[] buffer = new byte[width * height * BPP];
-
- CaptureZone captureZone = new(_indexCounter++, x, y, width, height, BPP, downscaleLevel, unscaledWidth, unscaledHeight, buffer);
- lock (_captureZones)
- InitializeCaptureZone(captureZone);
-
- return captureZone;
- }
-
- ///
- public bool UnregisterCaptureZone(CaptureZone captureZone)
- {
- lock (_captureZones)
- {
- if (_captureZones.TryGetValue(captureZone, out (ID3D11Texture2D stagingTexture, ID3D11Texture2D? scalingTexture, ID3D11ShaderResourceView? _scalingTextureView) data))
- {
- _captureZones.Remove(captureZone);
- data.stagingTexture.Dispose();
- data.scalingTexture?.Dispose();
- data._scalingTextureView?.Dispose();
-
- return true;
- }
-
+ Restart();
return false;
}
- }
- ///
- public void UpdateCaptureZone(CaptureZone captureZone, int? x = null, int? y = null, int? width = null, int? height = null, int? downscaleLevel = null)
- {
- lock (_captureZones)
- if (!_captureZones.ContainsKey(captureZone))
- throw new ArgumentException("The capture zone is not registered to this ScreenCapture", nameof(captureZone));
-
- int newX = x ?? captureZone.X;
- int newY = y ?? captureZone.Y;
- int newUnscaledWidth = width ?? captureZone.UnscaledWidth;
- int newUnscaledHeight = height ?? captureZone.UnscaledHeight;
- int newDownscaleLevel = downscaleLevel ?? captureZone.DownscaleLevel;
-
- ValidateCaptureZoneAndThrow(newX, newY, newUnscaledWidth, newUnscaledHeight);
-
- captureZone.X = newX;
- captureZone.Y = newY;
-
- //TODO DarthAffe 01.05.2022: For now just reinitialize the zone in that case, but this could be optimized to only recreate the textures needed.
- if ((width != null) || (height != null) || (downscaleLevel != null))
- {
- (int newWidth, int newHeight) = CalculateScaledSize(newUnscaledWidth, newUnscaledHeight, newDownscaleLevel);
- lock (_captureZones)
- {
- UnregisterCaptureZone(captureZone);
-
- captureZone.UnscaledWidth = newUnscaledWidth;
- captureZone.UnscaledHeight = newUnscaledHeight;
- captureZone.Width = newWidth;
- captureZone.Height = newHeight;
- captureZone.DownscaleLevel = newDownscaleLevel;
- captureZone.Buffer = new byte[newWidth * newHeight * BPP];
-
- InitializeCaptureZone(captureZone);
- }
- }
- }
-
- private (int width, int height) CalculateScaledSize(int width, int height, int downscaleLevel)
- {
- if (downscaleLevel > 0)
- for (int i = 0; i < downscaleLevel; i++)
- {
- width /= 2;
- height /= 2;
- }
-
- if (width < 1) width = 1;
- if (height < 1) height = 1;
-
- return (width, height);
- }
-
- private void ValidateCaptureZoneAndThrow(int x, int y, int width, int height)
- {
- if (_device == null) throw new ApplicationException("ScreenCapture isn't initialized.");
-
- if (x < 0) throw new ArgumentException("x < 0");
- if (y < 0) throw new ArgumentException("y < 0");
- if (width <= 0) throw new ArgumentException("with <= 0");
- if (height <= 0) throw new ArgumentException("height <= 0");
- if ((x + width) > Display.Width) throw new ArgumentException("x + width > Display width");
- if ((y + height) > Display.Height) throw new ArgumentException("y + height > Display height");
- }
-
- private void InitializeCaptureZone(in CaptureZone captureZone)
- {
- Texture2DDescription stagingTextureDesc = new()
- {
- CpuAccessFlags = CpuAccessFlags.Read,
- BindFlags = BindFlags.None,
- Format = Format.B8G8R8A8_UNorm,
- Width = captureZone.Width,
- Height = captureZone.Height,
- OptionFlags = ResourceOptionFlags.None,
- MipLevels = 1,
- ArraySize = 1,
- SampleDescription = { Count = 1, Quality = 0 },
- Usage = ResourceUsage.Staging
- };
- ID3D11Texture2D stagingTexture = _device!.CreateTexture2D(stagingTextureDesc);
-
- ID3D11Texture2D? scalingTexture = null;
- ID3D11ShaderResourceView? scalingTextureView = null;
- if (captureZone.DownscaleLevel > 0)
- {
- Texture2DDescription scalingTextureDesc = new()
- {
- CpuAccessFlags = CpuAccessFlags.None,
- BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
- Format = Format.B8G8R8A8_UNorm,
- Width = captureZone.UnscaledWidth,
- Height = captureZone.UnscaledHeight,
- OptionFlags = ResourceOptionFlags.GenerateMips,
- MipLevels = captureZone.DownscaleLevel + 1,
- ArraySize = 1,
- SampleDescription = { Count = 1, Quality = 0 },
- Usage = ResourceUsage.Default
- };
- scalingTexture = _device!.CreateTexture2D(scalingTextureDesc);
- scalingTextureView = _device.CreateShaderResourceView(scalingTexture);
- }
-
- _captureZones[captureZone] = (stagingTexture, scalingTexture, scalingTextureView);
- }
-
- ///
- public void Restart()
- {
- lock (_captureLock)
- {
- try
- {
- List captureZones = _captureZones.Keys.ToList();
- Dispose();
-
- using IDXGIAdapter1 adapter = _factory.GetAdapter1(Display.GraphicsCard.Index);
-
- D3D11.D3D11CreateDevice(adapter, DriverType.Unknown, DeviceCreationFlags.None, FEATURE_LEVELS, out _device).CheckError();
- _context = _device.ImmediateContext;
-
- _output = adapter.GetOutput(Display.Index);
- using IDXGIOutput5 output = _output.QueryInterface();
-
- Texture2DDescription captureTextureDesc = new()
- {
- CpuAccessFlags = CpuAccessFlags.None,
- BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
- Format = Format.B8G8R8A8_UNorm,
- Width = Display.Width,
- Height = Display.Height,
- OptionFlags = ResourceOptionFlags.None,
- MipLevels = 1,
- ArraySize = 1,
- SampleDescription = { Count = 1, Quality = 0 },
- Usage = ResourceUsage.Default
- };
- _captureTexture = _device.CreateTexture2D(captureTextureDesc);
-
- lock (_captureZones)
- {
- foreach (CaptureZone captureZone in captureZones)
- InitializeCaptureZone(captureZone);
- }
-
- if (_useNewDuplicationAdapter)
- _duplicatedOutput = output.DuplicateOutput1(_device, Format.B8G8R8A8_UNorm); // DarthAffe 27.02.2021: This prepares for the use of 10bit color depth
- else
- _duplicatedOutput = output.DuplicateOutput(_device);
- }
- catch { Dispose(false); }
- }
- }
-
- ///
- public void Dispose() => Dispose(true);
-
- private void Dispose(bool removeCaptureZones)
- {
try
{
- lock (_captureLock)
+ IDXGIResource? screenResource = null;
+ try
{
- try { _duplicatedOutput?.Dispose(); } catch { /**/ }
- _duplicatedOutput = null;
+ _duplicatedOutput.AcquireNextFrame(Timeout, out OutduplFrameInfo duplicateFrameInformation, out screenResource);
+ if ((screenResource == null) || (duplicateFrameInformation.LastPresentTime == 0)) return false;
+ using ID3D11Texture2D screenTexture = screenResource.QueryInterface();
+ _context.CopySubresourceRegion(_captureTexture, 0, 0, 0, 0, screenTexture, 0);
+ }
+ finally
+ {
try
{
- if (removeCaptureZones)
- {
- List captureZones = _captureZones.Keys.ToList();
- foreach (CaptureZone captureZone in captureZones)
- UnregisterCaptureZone(captureZone);
- }
+ screenResource?.Dispose();
+ _duplicatedOutput?.ReleaseFrame();
}
catch { /**/ }
+ }
- try { _output?.Dispose(); } catch { /**/ }
- try { _context?.Dispose(); } catch { /**/ }
- try { _device?.Dispose(); } catch { /**/ }
- try { _captureTexture?.Dispose(); } catch { /**/ }
- _context = null;
- _captureTexture = null;
+ result = true;
+ }
+ catch (SharpGenException dxException)
+ {
+ if ((dxException.ResultCode == ResultCode.AccessLost)
+ || (dxException.ResultCode == ResultCode.AccessDenied)
+ || (dxException.ResultCode == ResultCode.InvalidCall))
+ {
+ try
+ {
+ Restart();
+ }
+ catch { Thread.Sleep(100); }
}
}
catch { /**/ }
+
+ try
+ {
+ UpdateZones();
+ }
+ catch { /**/ }
+
+ try
+ {
+ Updated?.Invoke(this, new ScreenCaptureUpdatedEventArgs(result));
+ }
+ catch { /**/ }
+
+ return result;
+ }
+ }
+
+ private void UpdateZones()
+ {
+ if (_context == null) return;
+
+ lock (_captureZones)
+ {
+ foreach ((CaptureZone captureZone, (ID3D11Texture2D stagingTexture, ID3D11Texture2D? scalingTexture, ID3D11ShaderResourceView? scalingTextureView)) in _captureZones.Where(z => z.Key.AutoUpdate || z.Key.IsUpdateRequested))
+ {
+ if (scalingTexture != null)
+ {
+ _context.CopySubresourceRegion(scalingTexture, 0, 0, 0, 0, _captureTexture, 0,
+ new Box(captureZone.X, captureZone.Y, 0,
+ captureZone.X + captureZone.UnscaledWidth,
+ captureZone.Y + captureZone.UnscaledHeight, 1));
+ _context.GenerateMips(scalingTextureView);
+ _context.CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, scalingTexture, captureZone.DownscaleLevel);
+ }
+ else
+ _context.CopySubresourceRegion(stagingTexture, 0, 0, 0, 0, _captureTexture, 0,
+ new Box(captureZone.X, captureZone.Y, 0,
+ captureZone.X + captureZone.UnscaledWidth,
+ captureZone.Y + captureZone.UnscaledHeight, 1));
+
+ MappedSubresource mapSource = _context.Map(stagingTexture, 0, MapMode.Read, MapFlags.None);
+ IntPtr sourcePtr = mapSource.DataPointer;
+ lock (captureZone.Buffer)
+ {
+ for (int y = 0; y < captureZone.Height; y++)
+ {
+ Marshal.Copy(sourcePtr, captureZone.Buffer, y * captureZone.Stride, captureZone.Stride);
+ sourcePtr += mapSource.RowPitch;
+ }
+ }
+
+ _context.Unmap(stagingTexture, 0);
+ captureZone.SetUpdated();
+ }
+ }
+ }
+
+ ///
+ public CaptureZone RegisterCaptureZone(int x, int y, int width, int height, int downscaleLevel = 0)
+ {
+ ValidateCaptureZoneAndThrow(x, y, width, height);
+
+ int unscaledWidth = width;
+ int unscaledHeight = height;
+ (width, height) = CalculateScaledSize(unscaledWidth, unscaledHeight, downscaleLevel);
+
+ byte[] buffer = new byte[width * height * BPP];
+
+ CaptureZone captureZone = new(_indexCounter++, x, y, width, height, BPP, downscaleLevel, unscaledWidth, unscaledHeight, buffer);
+ lock (_captureZones)
+ InitializeCaptureZone(captureZone);
+
+ return captureZone;
+ }
+
+ ///
+ public bool UnregisterCaptureZone(CaptureZone captureZone)
+ {
+ lock (_captureZones)
+ {
+ if (_captureZones.TryGetValue(captureZone, out (ID3D11Texture2D stagingTexture, ID3D11Texture2D? scalingTexture, ID3D11ShaderResourceView? _scalingTextureView) data))
+ {
+ _captureZones.Remove(captureZone);
+ data.stagingTexture.Dispose();
+ data.scalingTexture?.Dispose();
+ data._scalingTextureView?.Dispose();
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ ///
+ public void UpdateCaptureZone(CaptureZone captureZone, int? x = null, int? y = null, int? width = null, int? height = null, int? downscaleLevel = null)
+ {
+ lock (_captureZones)
+ if (!_captureZones.ContainsKey(captureZone))
+ throw new ArgumentException("The capture zone is not registered to this ScreenCapture", nameof(captureZone));
+
+ int newX = x ?? captureZone.X;
+ int newY = y ?? captureZone.Y;
+ int newUnscaledWidth = width ?? captureZone.UnscaledWidth;
+ int newUnscaledHeight = height ?? captureZone.UnscaledHeight;
+ int newDownscaleLevel = downscaleLevel ?? captureZone.DownscaleLevel;
+
+ ValidateCaptureZoneAndThrow(newX, newY, newUnscaledWidth, newUnscaledHeight);
+
+ captureZone.X = newX;
+ captureZone.Y = newY;
+
+ //TODO DarthAffe 01.05.2022: For now just reinitialize the zone in that case, but this could be optimized to only recreate the textures needed.
+ if ((width != null) || (height != null) || (downscaleLevel != null))
+ {
+ (int newWidth, int newHeight) = CalculateScaledSize(newUnscaledWidth, newUnscaledHeight, newDownscaleLevel);
+ lock (_captureZones)
+ {
+ UnregisterCaptureZone(captureZone);
+
+ captureZone.UnscaledWidth = newUnscaledWidth;
+ captureZone.UnscaledHeight = newUnscaledHeight;
+ captureZone.Width = newWidth;
+ captureZone.Height = newHeight;
+ captureZone.DownscaleLevel = newDownscaleLevel;
+ captureZone.Buffer = new byte[newWidth * newHeight * BPP];
+
+ InitializeCaptureZone(captureZone);
+ }
+ }
+ }
+
+ private (int width, int height) CalculateScaledSize(int width, int height, int downscaleLevel)
+ {
+ if (downscaleLevel > 0)
+ for (int i = 0; i < downscaleLevel; i++)
+ {
+ width /= 2;
+ height /= 2;
+ }
+
+ if (width < 1) width = 1;
+ if (height < 1) height = 1;
+
+ return (width, height);
+ }
+
+ private void ValidateCaptureZoneAndThrow(int x, int y, int width, int height)
+ {
+ if (_device == null) throw new ApplicationException("ScreenCapture isn't initialized.");
+
+ if (x < 0) throw new ArgumentException("x < 0");
+ if (y < 0) throw new ArgumentException("y < 0");
+ if (width <= 0) throw new ArgumentException("with <= 0");
+ if (height <= 0) throw new ArgumentException("height <= 0");
+ if ((x + width) > Display.Width) throw new ArgumentException("x + width > Display width");
+ if ((y + height) > Display.Height) throw new ArgumentException("y + height > Display height");
+ }
+
+ private void InitializeCaptureZone(in CaptureZone captureZone)
+ {
+ Texture2DDescription stagingTextureDesc = new()
+ {
+ CpuAccessFlags = CpuAccessFlags.Read,
+ BindFlags = BindFlags.None,
+ Format = Format.B8G8R8A8_UNorm,
+ Width = captureZone.Width,
+ Height = captureZone.Height,
+ OptionFlags = ResourceOptionFlags.None,
+ MipLevels = 1,
+ ArraySize = 1,
+ SampleDescription = { Count = 1, Quality = 0 },
+ Usage = ResourceUsage.Staging
+ };
+ ID3D11Texture2D stagingTexture = _device!.CreateTexture2D(stagingTextureDesc);
+
+ ID3D11Texture2D? scalingTexture = null;
+ ID3D11ShaderResourceView? scalingTextureView = null;
+ if (captureZone.DownscaleLevel > 0)
+ {
+ Texture2DDescription scalingTextureDesc = new()
+ {
+ CpuAccessFlags = CpuAccessFlags.None,
+ BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
+ Format = Format.B8G8R8A8_UNorm,
+ Width = captureZone.UnscaledWidth,
+ Height = captureZone.UnscaledHeight,
+ OptionFlags = ResourceOptionFlags.GenerateMips,
+ MipLevels = captureZone.DownscaleLevel + 1,
+ ArraySize = 1,
+ SampleDescription = { Count = 1, Quality = 0 },
+ Usage = ResourceUsage.Default
+ };
+ scalingTexture = _device!.CreateTexture2D(scalingTextureDesc);
+ scalingTextureView = _device.CreateShaderResourceView(scalingTexture);
}
- #endregion
+ _captureZones[captureZone] = (stagingTexture, scalingTexture, scalingTextureView);
}
-}
+
+ ///
+ public void Restart()
+ {
+ lock (_captureLock)
+ {
+ try
+ {
+ List captureZones = _captureZones.Keys.ToList();
+ Dispose();
+
+ using IDXGIAdapter1 adapter = _factory.GetAdapter1(Display.GraphicsCard.Index) ?? throw new ApplicationException("Couldn't create DirectX-Adapter.");
+
+ D3D11.D3D11CreateDevice(adapter, DriverType.Unknown, DeviceCreationFlags.None, FEATURE_LEVELS, out _device).CheckError();
+ _context = _device!.ImmediateContext;
+
+ _output = adapter.GetOutput(Display.Index) ?? throw new ApplicationException("Couldn't get DirectX-Output.");
+ using IDXGIOutput5 output = _output.QueryInterface();
+
+ Texture2DDescription captureTextureDesc = new()
+ {
+ CpuAccessFlags = CpuAccessFlags.None,
+ BindFlags = BindFlags.RenderTarget | BindFlags.ShaderResource,
+ Format = Format.B8G8R8A8_UNorm,
+ Width = Display.Width,
+ Height = Display.Height,
+ OptionFlags = ResourceOptionFlags.None,
+ MipLevels = 1,
+ ArraySize = 1,
+ SampleDescription = { Count = 1, Quality = 0 },
+ Usage = ResourceUsage.Default
+ };
+ _captureTexture = _device.CreateTexture2D(captureTextureDesc);
+
+ lock (_captureZones)
+ {
+ foreach (CaptureZone captureZone in captureZones)
+ InitializeCaptureZone(captureZone);
+ }
+
+ if (_useNewDuplicationAdapter)
+ _duplicatedOutput = output.DuplicateOutput1(_device, Format.B8G8R8A8_UNorm); // DarthAffe 27.02.2021: This prepares for the use of 10bit color depth
+ else
+ _duplicatedOutput = output.DuplicateOutput(_device);
+ }
+ catch { Dispose(false); }
+ }
+ }
+
+ ///
+ public void Dispose() => Dispose(true);
+
+ private void Dispose(bool removeCaptureZones)
+ {
+ try
+ {
+ lock (_captureLock)
+ {
+ try { _duplicatedOutput?.Dispose(); } catch { /**/ }
+ _duplicatedOutput = null;
+
+ try
+ {
+ if (removeCaptureZones)
+ {
+ List captureZones = _captureZones.Keys.ToList();
+ foreach (CaptureZone captureZone in captureZones)
+ UnregisterCaptureZone(captureZone);
+ }
+ }
+ catch { /**/ }
+
+ try { _output?.Dispose(); } catch { /**/ }
+ try { _context?.Dispose(); } catch { /**/ }
+ try { _device?.Dispose(); } catch { /**/ }
+ try { _captureTexture?.Dispose(); } catch { /**/ }
+ _context = null;
+ _captureTexture = null;
+ }
+ }
+ catch { /**/ }
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs b/ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs
index 5a9a80b..da70944 100644
--- a/ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs
+++ b/ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs
@@ -2,83 +2,82 @@
using System.Collections.Generic;
using Vortice.DXGI;
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+/// Represents a using the .
+///
+public class DX11ScreenCaptureService : IScreenCaptureService
{
+ #region Properties & Fields
+
+ private readonly IDXGIFactory1 _factory;
+
+ private readonly Dictionary _screenCaptures = new();
+
+ #endregion
+
+ #region Constructors
+
///
- ///
+ /// Initializes a new instance of the class.
///
- public class DX11ScreenCaptureService : IScreenCaptureService
+ public DX11ScreenCaptureService()
{
- #region Properties & Fields
-
- private readonly IDXGIFactory1 _factory;
-
- private readonly Dictionary _screenCaptures = new();
-
- #endregion
-
- #region Constructors
-
- ///
- ///
- ///
- public DX11ScreenCaptureService()
- {
- DXGI.CreateDXGIFactory1(out _factory).CheckError();
- }
-
- #endregion
-
- #region Methods
-
- ///
- public IEnumerable GetGraphicsCards()
- {
- int i = 0;
- while (_factory.EnumAdapters1(i, out IDXGIAdapter1 adapter).Success)
- {
- yield return new GraphicsCard(i, adapter.Description1.Description, adapter.Description1.VendorId, adapter.Description1.DeviceId);
- adapter.Dispose();
- i++;
- }
- }
-
- ///
- public IEnumerable GetDisplays(GraphicsCard graphicsCard)
- {
- using IDXGIAdapter1? adapter = _factory.GetAdapter1(graphicsCard.Index);
-
- int i = 0;
- while (adapter.EnumOutputs(i, out IDXGIOutput output).Success)
- {
- int width = output.Description.DesktopCoordinates.Right - output.Description.DesktopCoordinates.Left;
- int height = output.Description.DesktopCoordinates.Bottom - output.Description.DesktopCoordinates.Top;
- yield return new Display(i, output.Description.DeviceName, width, height, graphicsCard);
- output.Dispose();
- i++;
- }
- }
-
- ///
- public IScreenCapture GetScreenCapture(Display display)
- {
- if (!_screenCaptures.TryGetValue(display, out DX11ScreenCapture? screenCapture))
- _screenCaptures.Add(display, screenCapture = new DX11ScreenCapture(_factory, display));
- return screenCapture;
- }
-
- ///
- public void Dispose()
- {
- foreach (DX11ScreenCapture screenCapture in _screenCaptures.Values)
- screenCapture.Dispose();
- _screenCaptures.Clear();
-
- _factory.Dispose();
-
- GC.SuppressFinalize(this);
- }
-
- #endregion
+ DXGI.CreateDXGIFactory1(out _factory!).CheckError();
}
-}
+
+ #endregion
+
+ #region Methods
+
+ ///
+ public IEnumerable GetGraphicsCards()
+ {
+ int i = 0;
+ while (_factory.EnumAdapters1(i, out IDXGIAdapter1 adapter).Success)
+ {
+ yield return new GraphicsCard(i, adapter.Description1.Description, adapter.Description1.VendorId, adapter.Description1.DeviceId);
+ adapter.Dispose();
+ i++;
+ }
+ }
+
+ ///
+ public IEnumerable GetDisplays(GraphicsCard graphicsCard)
+ {
+ using IDXGIAdapter1? adapter = _factory.GetAdapter1(graphicsCard.Index);
+
+ int i = 0;
+ while (adapter.EnumOutputs(i, out IDXGIOutput output).Success)
+ {
+ int width = output.Description.DesktopCoordinates.Right - output.Description.DesktopCoordinates.Left;
+ int height = output.Description.DesktopCoordinates.Bottom - output.Description.DesktopCoordinates.Top;
+ yield return new Display(i, output.Description.DeviceName, width, height, graphicsCard);
+ output.Dispose();
+ i++;
+ }
+ }
+
+ ///
+ public IScreenCapture GetScreenCapture(Display display)
+ {
+ if (!_screenCaptures.TryGetValue(display, out DX11ScreenCapture? screenCapture))
+ _screenCaptures.Add(display, screenCapture = new DX11ScreenCapture(_factory, display));
+ return screenCapture;
+ }
+
+ ///
+ public void Dispose()
+ {
+ foreach (DX11ScreenCapture screenCapture in _screenCaptures.Values)
+ screenCapture.Dispose();
+ _screenCaptures.Clear();
+
+ _factory.Dispose();
+
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/ScreenCapture.NET/Events/ScreenCaptureUpdatedEventArgs.cs b/ScreenCapture.NET/Events/ScreenCaptureUpdatedEventArgs.cs
index 2decab1..7fc89d2 100644
--- a/ScreenCapture.NET/Events/ScreenCaptureUpdatedEventArgs.cs
+++ b/ScreenCapture.NET/Events/ScreenCaptureUpdatedEventArgs.cs
@@ -2,34 +2,33 @@
using System;
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+///
+/// Represents the information supplied with an -event.
+///
+public class ScreenCaptureUpdatedEventArgs : EventArgs
{
- ///
+ #region Properties & Fields
+
///
- /// Represents the information supplied with an -event.
+ /// true if the update was successful; otherwise, false.
///
- public class ScreenCaptureUpdatedEventArgs : EventArgs
+ public bool IsSuccessful { get; set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Indicates if the last update was successful.
+ public ScreenCaptureUpdatedEventArgs(bool isSuccessful)
{
- #region Properties & Fields
-
- ///
- /// true if the update was successful; otherwise, false.
- ///
- public bool IsSuccessful { get; set; }
-
- #endregion
-
- #region Constructors
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// Indicates if the last update was successful.
- public ScreenCaptureUpdatedEventArgs(bool isSuccessful)
- {
- this.IsSuccessful = isSuccessful;
- }
-
- #endregion
+ this.IsSuccessful = isSuccessful;
}
-}
+
+ #endregion
+}
\ No newline at end of file
diff --git a/ScreenCapture.NET/Helper/DPIAwareness.cs b/ScreenCapture.NET/Helper/DPIAwareness.cs
index de55dd6..07bf69f 100644
--- a/ScreenCapture.NET/Helper/DPIAwareness.cs
+++ b/ScreenCapture.NET/Helper/DPIAwareness.cs
@@ -3,40 +3,39 @@
using System.Runtime.InteropServices;
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+/// Helper-class for DPI-related WIN-API calls.
+///
+public static class DPIAwareness
{
- ///
- /// Helper-class for DPI-related WIN-API calls.
- ///
- public static class DPIAwareness
+ [DllImport("user32.dll", SetLastError = true)]
+ internal static extern bool SetProcessDpiAwarenessContext(int dpiFlag);
+
+ [DllImport("SHCore.dll", SetLastError = true)]
+ internal static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);
+
+ [DllImport("user32.dll")]
+ internal static extern bool SetProcessDPIAware();
+
+ internal enum PROCESS_DPI_AWARENESS
{
- [DllImport("user32.dll", SetLastError = true)]
- internal static extern bool SetProcessDpiAwarenessContext(int dpiFlag);
-
- [DllImport("SHCore.dll", SetLastError = true)]
- internal static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);
-
- [DllImport("user32.dll")]
- internal static extern bool SetProcessDPIAware();
-
- internal enum PROCESS_DPI_AWARENESS
- {
- Process_DPI_Unaware = 0,
- Process_System_DPI_Aware = 1,
- Process_Per_Monitor_DPI_Aware = 2
- }
-
- internal enum DPI_AWARENESS_CONTEXT
- {
- DPI_AWARENESS_CONTEXT_UNAWARE = 16,
- DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 17,
- DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18,
- DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 34
- }
-
- ///
- /// Sets the DPI-Awareness-Context to V2. This is needed to prevent issues when using desktop duplication.
- ///
- public static void Initalize() => SetProcessDpiAwarenessContext((int)DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+ Process_DPI_Unaware = 0,
+ Process_System_DPI_Aware = 1,
+ Process_Per_Monitor_DPI_Aware = 2
}
-}
+
+ internal enum DPI_AWARENESS_CONTEXT
+ {
+ DPI_AWARENESS_CONTEXT_UNAWARE = 16,
+ DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 17,
+ DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18,
+ DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 34
+ }
+
+ ///
+ /// Sets the DPI-Awareness-Context to V2. This is needed to prevent issues when using desktop duplication.
+ ///
+ public static void Initalize() => SetProcessDpiAwarenessContext((int)DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
+}
\ No newline at end of file
diff --git a/ScreenCapture.NET/IScreenCapture.cs b/ScreenCapture.NET/IScreenCapture.cs
index e2b47e9..362836f 100644
--- a/ScreenCapture.NET/IScreenCapture.cs
+++ b/ScreenCapture.NET/IScreenCapture.cs
@@ -1,63 +1,62 @@
using System;
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+/// Represents the duplication of a single display.
+///
+public interface IScreenCapture : IDisposable
{
///
- /// Represents the duplication of a single display.
+ /// Gets the this capture is duplicating.
///
- public interface IScreenCapture : IDisposable
- {
- ///
- /// Gets the this capture is duplicating.
- ///
- Display Display { get; }
+ Display Display { get; }
- ///
- /// Occurs when the is updated.
- ///
- event EventHandler? Updated;
+ ///
+ /// Occurs when the is updated.
+ ///
+ event EventHandler? Updated;
- ///
- /// Attemts to capture the current frame showed on the .
- ///
- /// true if the current frame was captures successfully; otherwise, false.
- bool CaptureScreen();
+ ///
+ /// Attemts to capture the current frame showed on the .
+ ///
+ /// true if the current frame was captures successfully; otherwise, false.
+ bool CaptureScreen();
- ///
- /// Creates a new for this .
- ///
- /// The x-location of the region to capture (must be >= 0 and < screen-width).
- /// The y-location of the region to capture (must be >= 0 and < screen-height).
- /// The width of the region to capture (must be >= 0 and this + x must be <= screen-width).
- /// The height of the region to capture (must be >= 0 and this + y must be <= screen-height).
- /// The level of downscaling applied to the image of this region before copying to local memory. The calculation is (width and height)/2^downscaleLevel.
- /// The new .
- CaptureZone RegisterCaptureZone(int x, int y, int width, int height, int downscaleLevel = 0);
+ ///
+ /// Creates a new for this .
+ ///
+ /// The x-location of the region to capture (must be >= 0 and < screen-width).
+ /// The y-location of the region to capture (must be >= 0 and < screen-height).
+ /// The width of the region to capture (must be >= 0 and this + x must be <= screen-width).
+ /// The height of the region to capture (must be >= 0 and this + y must be <= screen-height).
+ /// The level of downscaling applied to the image of this region before copying to local memory. The calculation is (width and height)/2^downscaleLevel.
+ /// The new .
+ CaptureZone RegisterCaptureZone(int x, int y, int width, int height, int downscaleLevel = 0);
- ///
- /// Removes the given from the .
- ///
- /// The previously registered .
- /// true if the was successfully removed; otherwise, false.
- bool UnregisterCaptureZone(CaptureZone captureZone);
+ ///
+ /// Removes the given from the .
+ ///
+ /// The previously registered .
+ /// true if the was successfully removed; otherwise, false.
+ bool UnregisterCaptureZone(CaptureZone captureZone);
- ///
- /// Updates the the given .
- ///
- ///
- /// null-parameters are ignored and not changed.
- ///
- /// The previously registered .
- /// The new x-location of the region to capture (must be >= 0 and < screen-width).
- /// The new y-location of the region to capture (must be >= 0 and < screen-height).
- /// The width of the region to capture (must be >= 0 and this + x must be <= screen-width).
- /// The new height of the region to capture (must be >= 0 and this + y must be <= screen-height).
- /// The new level of downscaling applied to the image of this region before copying to local memory. The calculation is (width and height)/2^downscaleLevel.
- void UpdateCaptureZone(CaptureZone captureZone, int? x = null, int? y = null, int? width = null, int? height = null, int? downscaleLevel = null);
+ ///
+ /// Updates the the given .
+ ///
+ ///
+ /// null-parameters are ignored and not changed.
+ ///
+ /// The previously registered .
+ /// The new x-location of the region to capture (must be >= 0 and < screen-width).
+ /// The new y-location of the region to capture (must be >= 0 and < screen-height).
+ /// The width of the region to capture (must be >= 0 and this + x must be <= screen-width).
+ /// The new height of the region to capture (must be >= 0 and this + y must be <= screen-height).
+ /// The new level of downscaling applied to the image of this region before copying to local memory. The calculation is (width and height)/2^downscaleLevel.
+ void UpdateCaptureZone(CaptureZone captureZone, int? x = null, int? y = null, int? width = null, int? height = null, int? downscaleLevel = null);
- ///
- /// Restarts the .
- ///
- void Restart();
- }
-}
+ ///
+ /// Restarts the .
+ ///
+ void Restart();
+}
\ No newline at end of file
diff --git a/ScreenCapture.NET/IScreenCaptureService.cs b/ScreenCapture.NET/IScreenCaptureService.cs
index b4eb2fe..f98e28b 100644
--- a/ScreenCapture.NET/IScreenCaptureService.cs
+++ b/ScreenCapture.NET/IScreenCaptureService.cs
@@ -1,31 +1,30 @@
using System;
using System.Collections.Generic;
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+///
+///
+public interface IScreenCaptureService : IDisposable
{
///
- ///
+ /// Gets a enumerable of all available graphics-cards.
///
- public interface IScreenCaptureService : IDisposable
- {
- ///
- /// Gets a enumerable of all available graphics-cards.
- ///
- /// A enumerable of all available graphics-cards.
- IEnumerable GetGraphicsCards();
+ /// A enumerable of all available graphics-cards.
+ IEnumerable GetGraphicsCards();
- ///
- /// Gets a enumerable of all display connected to the given graphics-cards.
- ///
- /// The graphics-card to get the displays from.
- /// A enumerable of all display connected to the given graphics-cards.
- IEnumerable GetDisplays(GraphicsCard graphicsCard);
+ ///
+ /// Gets a enumerable of all display connected to the given graphics-cards.
+ ///
+ /// The graphics-card to get the displays from.
+ /// A enumerable of all display connected to the given graphics-cards.
+ IEnumerable GetDisplays(GraphicsCard graphicsCard);
- ///
- /// Creates a for the given display.
- ///
- /// The display to duplicate.
- /// The for the give display.
- IScreenCapture GetScreenCapture(Display display);
- }
-}
+ ///
+ /// Creates a for the given display.
+ ///
+ /// The display to duplicate.
+ /// The for the give display.
+ IScreenCapture GetScreenCapture(Display display);
+}
\ No newline at end of file
diff --git a/ScreenCapture.NET/Model/BlackBarDetection.cs b/ScreenCapture.NET/Model/BlackBarDetection.cs
index 7d58e5b..254a57c 100644
--- a/ScreenCapture.NET/Model/BlackBarDetection.cs
+++ b/ScreenCapture.NET/Model/BlackBarDetection.cs
@@ -2,141 +2,140 @@
using System;
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+/// Represents the configuration for the detection and removal of black bars around the screen image.
+///
+public sealed class BlackBarDetection
{
+ #region Properties & Fields
+
+ private readonly CaptureZone _captureZone;
+
+ private int? _top;
///
- /// Represents the configuration for the detection and removal of black bars around the screen image.
+ /// Gets the size of the detected black bar at the top of the image.
///
- public sealed class BlackBarDetection
+ public int Top => _top ??= CalculateTop();
+
+ private int? _bottom;
+ ///
+ /// Gets the size of the detected black bar at the bottom of the image.
+ ///
+ public int Bottom => _bottom ??= CalculateBottom();
+
+ private int? _left;
+ ///
+ /// Gets the size of the detected black bar at the left of the image.
+ ///
+ public int Left => _left ??= CalculateLeft();
+
+ private int? _right;
+ ///
+ /// Gets the size of the detected black bar at the right of the image.
+ ///
+ public int Right => _right ??= CalculateRight();
+
+ private int _theshold = 0;
+ ///
+ /// Gets or sets the threshold of "blackness" used to detect black bars. (e. g. Threshold 5 will consider a pixel of color [5,5,5] as black.) (default 0)
+ ///
+ public int Threshold
{
- #region Properties & Fields
-
- private readonly CaptureZone _captureZone;
-
- private int? _top;
- ///
- /// Gets the size of the detected black bar at the top of the image.
- ///
- public int Top => _top ??= CalculateTop();
-
- private int? _bottom;
- ///
- /// Gets the size of the detected black bar at the bottom of the image.
- ///
- public int Bottom => _bottom ??= CalculateBottom();
-
- private int? _left;
- ///
- /// Gets the size of the detected black bar at the left of the image.
- ///
- public int Left => _left ??= CalculateLeft();
-
- private int? _right;
- ///
- /// Gets the size of the detected black bar at the right of the image.
- ///
- public int Right => _right ??= CalculateRight();
-
- private int _theshold = 0;
- ///
- /// Gets or sets the threshold of "blackness" used to detect black bars. (e. g. Threshold 5 will consider a pixel of color [5,5,5] as black.) (default 0)
- ///
- public int Threshold
+ get => _theshold;
+ set
{
- get => _theshold;
- set
- {
- _theshold = value;
- InvalidateCache();
- }
+ _theshold = value;
+ InvalidateCache();
+ }
+ }
+
+ #endregion
+
+ #region Constructors
+
+ internal BlackBarDetection(CaptureZone captureZone)
+ {
+ this._captureZone = captureZone;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Invalidates the cached values and recalculates , , and .
+ ///
+ public void InvalidateCache()
+ {
+ _top = null;
+ _bottom = null;
+ _left = null;
+ _right = null;
+ }
+
+ private int CalculateTop()
+ {
+ int threshold = Threshold;
+ int stride = _captureZone.Stride;
+ for (int row = 0; row < _captureZone.Height; row++)
+ {
+ Span data = new(_captureZone.Buffer, row * stride, stride);
+ for (int i = 0; i < data.Length; i += 4)
+ if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
+ return row;
}
- #endregion
+ return 0;
+ }
- #region Constructors
-
- internal BlackBarDetection(CaptureZone captureZone)
+ private int CalculateBottom()
+ {
+ int threshold = Threshold;
+ int stride = _captureZone.Stride;
+ for (int row = _captureZone.Height - 1; row >= 0; row--)
{
- this._captureZone = captureZone;
+ Span data = new(_captureZone.Buffer, row * stride, stride);
+ for (int i = 0; i < data.Length; i += 4)
+ if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
+ return (_captureZone.Height - 1) - row;
}
- #endregion
+ return 0;
+ }
- #region Methods
-
- ///
- /// Invalidates the cached values and recalculates , , and .
- ///
- public void InvalidateCache()
- {
- _top = null;
- _bottom = null;
- _left = null;
- _right = null;
- }
-
- private int CalculateTop()
- {
- int threshold = Threshold;
- int stride = _captureZone.Stride;
+ private int CalculateLeft()
+ {
+ int threshold = Threshold;
+ int stride = _captureZone.Stride;
+ byte[] buffer = _captureZone.Buffer;
+ for (int column = 0; column < _captureZone.Width; column++)
for (int row = 0; row < _captureZone.Height; row++)
{
- Span data = new(_captureZone.Buffer, row * stride, stride);
- for (int i = 0; i < data.Length; i += 4)
- if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
- return row;
+ int offset = (stride * row) + (column * 4);
+ if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
+ return column;
}
- return 0;
- }
-
- private int CalculateBottom()
- {
- int threshold = Threshold;
- int stride = _captureZone.Stride;
- for (int row = _captureZone.Height - 1; row >= 0; row--)
- {
- Span data = new(_captureZone.Buffer, row * stride, stride);
- for (int i = 0; i < data.Length; i += 4)
- if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
- return (_captureZone.Height - 1) - row;
- }
-
- return 0;
- }
-
- private int CalculateLeft()
- {
- int threshold = Threshold;
- int stride = _captureZone.Stride;
- byte[] buffer = _captureZone.Buffer;
- for (int column = 0; column < _captureZone.Width; column++)
- for (int row = 0; row < _captureZone.Height; row++)
- {
- int offset = (stride * row) + (column * 4);
- if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
- return column;
- }
-
- return 0;
- }
-
- private int CalculateRight()
- {
- int threshold = Threshold;
- int stride = _captureZone.Stride;
- byte[] buffer = _captureZone.Buffer;
- for (int column = _captureZone.Width - 1; column >= 0; column--)
- for (int row = 0; row < _captureZone.Height; row++)
- {
- int offset = (stride * row) + (column * 4);
- if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
- return (_captureZone.Width - 1) - column;
- }
-
- return 0;
- }
-
- #endregion
+ return 0;
}
-}
+
+ private int CalculateRight()
+ {
+ int threshold = Threshold;
+ int stride = _captureZone.Stride;
+ byte[] buffer = _captureZone.Buffer;
+ for (int column = _captureZone.Width - 1; column >= 0; column--)
+ for (int row = 0; row < _captureZone.Height; row++)
+ {
+ int offset = (stride * row) + (column * 4);
+ if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
+ return (_captureZone.Width - 1) - column;
+ }
+
+ return 0;
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/ScreenCapture.NET/Model/CaptureZone.cs b/ScreenCapture.NET/Model/CaptureZone.cs
index 460a0a6..100263d 100644
--- a/ScreenCapture.NET/Model/CaptureZone.cs
+++ b/ScreenCapture.NET/Model/CaptureZone.cs
@@ -2,161 +2,160 @@
using System;
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+/// Represents a duplicated region on the screen.
+///
+public sealed class CaptureZone
{
+ #region Properties & Fields
+
///
- /// Represents a duplicated region on the screen.
+ /// Gets the unique id of this .
///
- public sealed class CaptureZone
+ public int Id { get; }
+
+ ///
+ /// Gets the x-location of the region on the screen.
+ ///
+ public int X { get; internal set; }
+
+ ///
+ /// Gets the y-location of the region on the screen.
+ ///
+ public int Y { get; internal set; }
+
+ ///
+ /// Gets the width of the captured region.
+ ///
+ public int Width { get; internal set; }
+
+ ///
+ /// Gets the height of the captured region.
+ ///
+ public int Height { get; internal set; }
+
+ ///
+ /// Gets the level of downscaling applied to the image of this region before copying to local memory. The calculation is (width and height)/2^downscaleLevel.
+ ///
+ public int DownscaleLevel { get; internal set; }
+
+ ///
+ /// Gets the original width of the region (this equals if is 0).
+ ///
+ public int UnscaledWidth { get; internal set; }
+
+ ///
+ /// Gets the original height of the region (this equals if is 0).
+ ///
+ public int UnscaledHeight { get; internal set; }
+
+ ///
+ /// Gets the amount of bytes per pixel in the image (most likely 3 [RGB] or 4 [ARGB]).
+ ///
+ public int BytesPerPixel { get; }
+
+ ///
+ /// Gets the size in bytes of a row in the region ( * ).
+ ///
+ public int Stride => Width * BytesPerPixel;
+
+ ///
+ /// Gets the buffer containing the image data. Format depends on the specific capture but is most likely BGRA32.
+ ///
+ public byte[] Buffer { get; internal set; }
+
+ ///
+ /// Gets the config for black-bar detection.
+ ///
+ public BlackBarDetection BlackBars { get; }
+
+ ///
+ /// Gets or sets if the should be automatically updated on every captured frame.
+ ///
+ public bool AutoUpdate { get; set; } = true;
+
+ ///
+ /// Gets if an update for the is requested on the next captured frame.
+ ///
+ public bool IsUpdateRequested { get; private set; }
+
+ #endregion
+
+ #region Events
+
+ ///
+ /// Occurs when the is updated.
+ ///
+ public event EventHandler? Updated;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The unique id of this .
+ /// The x-location of the region on the screen.
+ /// The y-location of the region on the screen.
+ /// The width of the region on the screen.
+ /// The height of the region on the screen.
+ /// The level of downscaling applied to the image of this region before copying to local memory.
+ /// The original width of the region.
+ /// The original height of the region
+ /// The buffer containing the image data.
+ internal CaptureZone(int id, int x, int y, int width, int height, int bytesPerPixel, int downscaleLevel, int unscaledWidth, int unscaledHeight, byte[] buffer)
{
- #region Properties & Fields
+ this.Id = id;
+ this.X = x;
+ this.Y = y;
+ this.Width = width;
+ this.Height = height;
+ this.BytesPerPixel = bytesPerPixel;
+ this.UnscaledWidth = unscaledWidth;
+ this.UnscaledHeight = unscaledHeight;
+ this.DownscaleLevel = downscaleLevel;
+ this.Buffer = buffer;
- ///
- /// Gets the unique id of this .
- ///
- public int Id { get; }
-
- ///
- /// Gets the x-location of the region on the screen.
- ///
- public int X { get; internal set; }
-
- ///
- /// Gets the y-location of the region on the screen.
- ///
- public int Y { get; internal set; }
-
- ///
- /// Gets the width of the captured region.
- ///
- public int Width { get; internal set; }
-
- ///
- /// Gets the height of the captured region.
- ///
- public int Height { get; internal set; }
-
- ///
- /// Gets the level of downscaling applied to the image of this region before copying to local memory. The calculation is (width and height)/2^downscaleLevel.
- ///
- public int DownscaleLevel { get; internal set; }
-
- ///
- /// Gets the original width of the region (this equals if is 0).
- ///
- public int UnscaledWidth { get; internal set; }
-
- ///
- /// Gets the original height of the region (this equals if is 0).
- ///
- public int UnscaledHeight { get; internal set; }
-
- ///
- /// Gets the amount of bytes per pixel in the image (most likely 3 [RGB] or 4 [ARGB]).
- ///
- public int BytesPerPixel { get; }
-
- ///
- /// Gets the size in bytes of a row in the region ( * ).
- ///
- public int Stride => Width * BytesPerPixel;
-
- ///
- /// Gets the buffer containing the image data. Format depends on the specific capture but is most likely BGRA32.
- ///
- public byte[] Buffer { get; internal set; }
-
- ///
- /// Gets the config for black-bar detection.
- ///
- public BlackBarDetection BlackBars { get; }
-
- ///
- /// Gets or sets if the should be automatically updated on every captured frame.
- ///
- public bool AutoUpdate { get; set; } = true;
-
- ///
- /// Gets if an update for the is requested on the next captured frame.
- ///
- public bool IsUpdateRequested { get; private set; }
-
- #endregion
-
- #region Events
-
- ///
- /// Occurs when the is updated.
- ///
- public event EventHandler? Updated;
-
- #endregion
-
- #region Constructors
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The unique id of this .
- /// The x-location of the region on the screen.
- /// The y-location of the region on the screen.
- /// The width of the region on the screen.
- /// The height of the region on the screen.
- /// The level of downscaling applied to the image of this region before copying to local memory.
- /// The original width of the region.
- /// The original height of the region
- /// The buffer containing the image data.
- internal CaptureZone(int id, int x, int y, int width, int height, int bytesPerPixel, int downscaleLevel, int unscaledWidth, int unscaledHeight, byte[] buffer)
- {
- this.Id = id;
- this.X = x;
- this.Y = y;
- this.Width = width;
- this.Height = height;
- this.BytesPerPixel = bytesPerPixel;
- this.UnscaledWidth = unscaledWidth;
- this.UnscaledHeight = unscaledHeight;
- this.DownscaleLevel = downscaleLevel;
- this.Buffer = buffer;
-
- BlackBars = new BlackBarDetection(this);
- }
-
- #endregion
-
- #region Methods
-
- ///
- /// Requests to update this when the next frame is captured.
- /// Only necessary if is set to false.
- ///
- public void RequestUpdate() => IsUpdateRequested = true;
-
- ///
- /// Marks the as updated.
- /// WARNING: This should not be called outside of an !
- ///
- public void SetUpdated()
- {
- IsUpdateRequested = false;
- BlackBars.InvalidateCache();
-
- Updated?.Invoke(this, new EventArgs());
- }
-
- ///
- /// Determines whether this equals the given one.
- ///
- /// The to compare.
- /// true if the specified object is equal to the current object; otherwise, false.
- public bool Equals(CaptureZone other) => Id == other.Id;
-
- ///
- public override bool Equals(object? obj) => obj is CaptureZone other && Equals(other);
-
- ///
- public override int GetHashCode() => Id;
-
- #endregion
+ BlackBars = new BlackBarDetection(this);
}
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Requests to update this when the next frame is captured.
+ /// Only necessary if is set to false.
+ ///
+ public void RequestUpdate() => IsUpdateRequested = true;
+
+ ///
+ /// Marks the as updated.
+ /// WARNING: This should not be called outside of an !
+ ///
+ public void SetUpdated()
+ {
+ IsUpdateRequested = false;
+ BlackBars.InvalidateCache();
+
+ Updated?.Invoke(this, new EventArgs());
+ }
+
+ ///
+ /// Determines whether this equals the given one.
+ ///
+ /// The to compare.
+ /// true if the specified object is equal to the current object; otherwise, false.
+ public bool Equals(CaptureZone other) => Id == other.Id;
+
+ ///
+ public override bool Equals(object? obj) => obj is CaptureZone other && Equals(other);
+
+ ///
+ public override int GetHashCode() => Id;
+
+ #endregion
}
\ No newline at end of file
diff --git a/ScreenCapture.NET/Model/Display.cs b/ScreenCapture.NET/Model/Display.cs
index e2b5ed0..7c749aa 100644
--- a/ScreenCapture.NET/Model/Display.cs
+++ b/ScreenCapture.NET/Model/Display.cs
@@ -1,93 +1,92 @@
// ReSharper disable MemberCanBePrivate.Global
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+/// Represents a display connected to graphics-card.
+///
+public readonly struct Display
{
+ #region Properties & Fields
+
///
- /// Represents a display connected to graphics-card.
+ /// Gets the index of the .
///
- public readonly struct Display
+ public int Index { get; }
+
+ ///
+ /// Gets the name of the .
+ ///
+ public string DeviceName { get; }
+
+ ///
+ /// Gets the with of the .
+ ///
+ public int Width { get; }
+
+ ///
+ /// Gets the height of the .
+ ///
+ public int Height { get; }
+
+ ///
+ /// Gets the this is connected to.
+ ///
+ public GraphicsCard GraphicsCard { get; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The index of the .
+ /// The name of the .
+ /// The with of the .
+ /// The height of the .
+ /// The this is connected to.
+ public Display(int index, string deviceName, int width, int height, GraphicsCard graphicsCard)
{
- #region Properties & Fields
-
- ///
- /// Gets the index of the .
- ///
- public int Index { get; }
-
- ///
- /// Gets the name of the .
- ///
- public string DeviceName { get; }
-
- ///
- /// Gets the with of the .
- ///
- public int Width { get; }
-
- ///
- /// Gets the height of the .
- ///
- public int Height { get; }
-
- ///
- /// Gets the this is connected to.
- ///
- public GraphicsCard GraphicsCard { get; }
-
- #endregion
-
- #region Constructors
-
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The index of the .
- /// The name of the .
- /// The with of the .
- /// The height of the .
- /// The this is connected to.
- public Display(int index, string deviceName, int width, int height, GraphicsCard graphicsCard)
- {
- this.Index = index;
- this.DeviceName = deviceName;
- this.Width = width;
- this.Height = height;
- this.GraphicsCard = graphicsCard;
- }
-
- #endregion
-
- #region Methods
-
- ///
- /// Determines whether this equals the given one.
- ///
- /// The to compare.
- /// true if the specified object is equal to the current object; otherwise, false.
- public bool Equals(Display other) => Index == other.Index;
-
- ///
- public override bool Equals(object? obj) => obj is Display other && Equals(other);
-
- ///
- public override int GetHashCode() => Index;
-
- ///
- /// Determines whether two are equal.
- ///
- /// The first value.
- /// The second value.
- /// true if the two specified displays are equal; otherwise, false.
- public static bool operator ==(Display left, Display right) => left.Equals(right);
-
- ///
- /// Determines whether two are not equal.
- ///
- /// The first value.
- /// The second value.
- /// true if the two specified displays are not equal; otherwise, false.
- public static bool operator !=(Display left, Display right) => !(left == right);
-
- #endregion
+ this.Index = index;
+ this.DeviceName = deviceName;
+ this.Width = width;
+ this.Height = height;
+ this.GraphicsCard = graphicsCard;
}
-}
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Determines whether this equals the given one.
+ ///
+ /// The to compare.
+ /// true if the specified object is equal to the current object; otherwise, false.
+ public bool Equals(Display other) => Index == other.Index;
+
+ ///
+ public override bool Equals(object? obj) => obj is Display other && Equals(other);
+
+ ///
+ public override int GetHashCode() => Index;
+
+ ///
+ /// Determines whether two are equal.
+ ///
+ /// The first value.
+ /// The second value.
+ /// true if the two specified displays are equal; otherwise, false.
+ public static bool operator ==(Display left, Display right) => left.Equals(right);
+
+ ///
+ /// Determines whether two are not equal.
+ ///
+ /// The first value.
+ /// The second value.
+ /// true if the two specified displays are not equal; otherwise, false.
+ public static bool operator !=(Display left, Display right) => !(left == right);
+
+ #endregion
+}
\ No newline at end of file
diff --git a/ScreenCapture.NET/Model/GraphicsCard.cs b/ScreenCapture.NET/Model/GraphicsCard.cs
index 2c4d27d..5b7ba3c 100644
--- a/ScreenCapture.NET/Model/GraphicsCard.cs
+++ b/ScreenCapture.NET/Model/GraphicsCard.cs
@@ -1,86 +1,85 @@
// ReSharper disable MemberCanBePrivate.Global
-namespace ScreenCapture.NET
+namespace ScreenCapture.NET;
+
+///
+/// Represents a graphics-card.
+///
+public readonly struct GraphicsCard
{
+ #region Properties & Fields
+
///
- /// Represents a graphics-card.
+ /// Gets the index of the .
///
- public readonly struct GraphicsCard
+ public int Index { get; }
+
+ ///
+ /// Gets the name of the .
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets the vendor-id of the .
+ ///
+ public int VendorId { get; }
+
+ ///
+ /// Gets the device-id of the .
+ ///
+ public int DeviceId { get; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The index of the .
+ /// The name of the .
+ /// The vendor-id of the .
+ /// The device-id of the .
+ public GraphicsCard(int index, string name, int vendorId, int deviceId)
{
- #region Properties & Fields
-
- ///
- /// Gets the index of the .
- ///
- public int Index { get; }
-
- ///
- /// Gets the name of the .
- ///
- public string Name { get; }
-
- ///
- /// Gets the vendor-id of the .
- ///
- public int VendorId { get; }
-
- ///
- /// Gets the device-id of the .
- ///
- public int DeviceId { get; }
-
- #endregion
-
- #region Constructors
-
- ///
- /// Initializes a new instance of the struct.
- ///
- /// The index of the .
- /// The name of the .
- /// The vendor-id of the .
- /// The device-id of the .
- public GraphicsCard(int index, string name, int vendorId, int deviceId)
- {
- this.Index = index;
- this.Name = name;
- this.VendorId = vendorId;
- this.DeviceId = deviceId;
- }
-
- #endregion
-
- #region Methods
-
- ///
- /// Determines whether this equals the given one.
- ///
- /// The to compare.
- /// true if the specified object is equal to the current object; otherwise, false.
- public bool Equals(GraphicsCard other) => Index == other.Index;
-
- ///
- public override bool Equals(object? obj) => obj is GraphicsCard other && Equals(other);
-
- ///
- public override int GetHashCode() => Index;
-
- ///
- /// Determines whether two are equal.
- ///
- /// The first value.
- /// The second value.
- /// true if the two specified graphics-cards are equal; otherwise, false.
- public static bool operator ==(GraphicsCard left, GraphicsCard right) => left.Equals(right);
-
- ///
- /// Determines whether two are not equal.
- ///
- /// The first value.
- /// The second value.
- /// true if the two specified graphics-cards are not equal; otherwise, false.
- public static bool operator !=(GraphicsCard left, GraphicsCard right) => !(left == right);
-
- #endregion
+ this.Index = index;
+ this.Name = name;
+ this.VendorId = vendorId;
+ this.DeviceId = deviceId;
}
-}
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Determines whether this equals the given one.
+ ///
+ /// The to compare.
+ /// true if the specified object is equal to the current object; otherwise, false.
+ public bool Equals(GraphicsCard other) => Index == other.Index;
+
+ ///
+ public override bool Equals(object? obj) => obj is GraphicsCard other && Equals(other);
+
+ ///
+ public override int GetHashCode() => Index;
+
+ ///
+ /// Determines whether two are equal.
+ ///
+ /// The first value.
+ /// The second value.
+ /// true if the two specified graphics-cards are equal; otherwise, false.
+ public static bool operator ==(GraphicsCard left, GraphicsCard right) => left.Equals(right);
+
+ ///
+ /// Determines whether two are not equal.
+ ///
+ /// The first value.
+ /// The second value.
+ /// true if the two specified graphics-cards are not equal; otherwise, false.
+ public static bool operator !=(GraphicsCard left, GraphicsCard right) => !(left == right);
+
+ #endregion
+}
\ No newline at end of file