Skip to content

Commit 8ce7db1

Browse files
committed
Reworked capturing to fully detach the capture zone from the capturing itself and allow better handling of result-data (WIP)
1 parent 9066366 commit 8ce7db1

16 files changed

+803
-356
lines changed

ScreenCapture.NET.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BGRA/@EntryIndexedValue">BGRA</s:String>
23
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DPI/@EntryIndexedValue">DPI</s:String>
34
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DX/@EntryIndexedValue">DX</s:String></wpf:ResourceDictionary>

ScreenCapture.NET/DirectX/DX11ScreenCapture.cs

Lines changed: 237 additions & 269 deletions
Large diffs are not rendered by default.

ScreenCapture.NET/DirectX/DX11ScreenCaptureService.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public IEnumerable<Display> GetDisplays(GraphicsCard graphicsCard)
5959
}
6060
}
6161

62-
private Rotation GetRotation(ModeRotation rotation) => rotation switch
62+
private static Rotation GetRotation(ModeRotation rotation) => rotation switch
6363
{
6464
ModeRotation.Rotate90 => Rotation.Rotation90,
6565
ModeRotation.Rotate180 => Rotation.Rotation180,
@@ -68,7 +68,8 @@ public IEnumerable<Display> GetDisplays(GraphicsCard graphicsCard)
6868
};
6969

7070
/// <inheritdoc />
71-
public IScreenCapture GetScreenCapture(Display display)
71+
IScreenCapture IScreenCaptureService.GetScreenCapture(Display display) => GetScreenCapture(display);
72+
public DX11ScreenCapture GetScreenCapture(Display display)
7273
{
7374
if (!_screenCaptures.TryGetValue(display, out DX11ScreenCapture? screenCapture))
7475
_screenCaptures.Add(display, screenCapture = new DX11ScreenCapture(_factory, display));
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace ScreenCapture.NET;
6+
7+
public abstract class AbstractScreenCapture<TColor> : IScreenCapture
8+
where TColor : struct, IColor
9+
{
10+
#region Properties & Fields
11+
12+
private bool _isDisposed;
13+
private int _indexCounter = 0;
14+
15+
protected HashSet<CaptureZone<TColor>> CaptureZones { get; } = new();
16+
17+
public Display Display { get; }
18+
19+
#endregion
20+
21+
#region Events
22+
23+
public event EventHandler<ScreenCaptureUpdatedEventArgs>? Updated;
24+
25+
#endregion
26+
27+
#region Constructors
28+
29+
protected AbstractScreenCapture(Display display)
30+
{
31+
this.Display = display;
32+
}
33+
34+
~AbstractScreenCapture() => Dispose(false);
35+
36+
#endregion
37+
38+
#region Methods
39+
40+
public virtual bool CaptureScreen()
41+
{
42+
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
43+
44+
bool result;
45+
46+
try
47+
{
48+
result = PerformScreenCapture();
49+
}
50+
catch
51+
{
52+
result = false;
53+
}
54+
55+
foreach (CaptureZone<TColor> captureZone in CaptureZones.Where(x => x.AutoUpdate || x.IsUpdateRequested))
56+
{
57+
try
58+
{
59+
PerformCaptureZoneUpdate(captureZone);
60+
captureZone.SetUpdated();
61+
}
62+
catch { /* */ }
63+
}
64+
65+
OnUpdated(result);
66+
67+
return result;
68+
}
69+
70+
protected abstract bool PerformScreenCapture();
71+
72+
protected abstract void PerformCaptureZoneUpdate(CaptureZone<TColor> captureZone);
73+
74+
protected virtual void OnUpdated(bool result)
75+
{
76+
try
77+
{
78+
Updated?.Invoke(this, new ScreenCaptureUpdatedEventArgs(result));
79+
}
80+
catch { /**/ }
81+
}
82+
83+
ICaptureZone IScreenCapture.RegisterCaptureZone(int x, int y, int width, int height, int downscaleLevel) => RegisterCaptureZone(x, y, width, height, downscaleLevel);
84+
public virtual CaptureZone<TColor> RegisterCaptureZone(int x, int y, int width, int height, int downscaleLevel = 0)
85+
{
86+
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
87+
88+
lock (CaptureZones)
89+
{
90+
ValidateCaptureZoneAndThrow(x, y, width, height, downscaleLevel);
91+
92+
int unscaledWidth = width;
93+
int unscaledHeight = height;
94+
(width, height, downscaleLevel) = CalculateScaledSize(unscaledWidth, unscaledHeight, downscaleLevel);
95+
96+
#if NET7_0_OR_GREATER
97+
CaptureZone<TColor> captureZone = new(_indexCounter++, Display, x, y, width, height, downscaleLevel, unscaledWidth, unscaledHeight, new ScreenImage<TColor>(width, height, TColor.ColorFormat));
98+
#else
99+
CaptureZone<TColor> captureZone = new(_indexCounter++, Display, x, y, width, height, downscaleLevel, unscaledWidth, unscaledHeight, new ScreenImage<TColor>(width, height, IColor.GetColorFormat<TColor>()));
100+
#endif
101+
CaptureZones.Add(captureZone);
102+
103+
return captureZone;
104+
}
105+
}
106+
107+
protected virtual void ValidateCaptureZoneAndThrow(int x, int y, int width, int height, int downscaleLevel)
108+
{
109+
if (x < 0) throw new ArgumentException("x < 0");
110+
if (y < 0) throw new ArgumentException("y < 0");
111+
if (width <= 0) throw new ArgumentException("with <= 0");
112+
if (height <= 0) throw new ArgumentException("height <= 0");
113+
if ((x + width) > Display.Width) throw new ArgumentException("x + width > Display width");
114+
if ((y + height) > Display.Height) throw new ArgumentException("y + height > Display height");
115+
}
116+
117+
protected virtual (int width, int height, int downscaleLevel) CalculateScaledSize(int width, int height, int downscaleLevel)
118+
{
119+
if (downscaleLevel > 0)
120+
for (int i = 0; i < downscaleLevel; i++)
121+
{
122+
if ((width <= 1) && (height <= 1))
123+
{
124+
downscaleLevel = i;
125+
break;
126+
}
127+
128+
width /= 2;
129+
height /= 2;
130+
}
131+
132+
if (width < 1) width = 1;
133+
if (height < 1) height = 1;
134+
135+
return (width, height, downscaleLevel);
136+
}
137+
138+
bool IScreenCapture.UnregisterCaptureZone(ICaptureZone captureZone) => UnregisterCaptureZone(captureZone as CaptureZone<TColor> ?? throw new ArgumentException("Invalid capture-zone."));
139+
public virtual bool UnregisterCaptureZone(CaptureZone<TColor> captureZone)
140+
{
141+
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
142+
143+
return CaptureZones.Remove(captureZone);
144+
}
145+
146+
void IScreenCapture.UpdateCaptureZone(ICaptureZone captureZone, int? x, int? y, int? width, int? height, int? downscaleLevel)
147+
=> UpdateCaptureZone(captureZone as CaptureZone<TColor> ?? throw new ArgumentException("Invalid capture-zone."), x, y, width, height, downscaleLevel);
148+
public virtual void UpdateCaptureZone(CaptureZone<TColor> captureZone, int? x = null, int? y = null, int? width = null, int? height = null, int? downscaleLevel = null)
149+
{
150+
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
151+
152+
lock (CaptureZones)
153+
{
154+
if (!CaptureZones.Contains(captureZone))
155+
throw new ArgumentException("The capture zone is not registered to this ScreenCapture", nameof(captureZone));
156+
157+
int newX = x ?? captureZone.X;
158+
int newY = y ?? captureZone.Y;
159+
int newUnscaledWidth = width ?? captureZone.UnscaledWidth;
160+
int newUnscaledHeight = height ?? captureZone.UnscaledHeight;
161+
int newDownscaleLevel = downscaleLevel ?? captureZone.DownscaleLevel;
162+
163+
ValidateCaptureZoneAndThrow(newX, newY, newUnscaledWidth, newUnscaledHeight, newDownscaleLevel);
164+
165+
captureZone.X = newX;
166+
captureZone.Y = newY;
167+
168+
if ((width != null) || (height != null) || (downscaleLevel != null))
169+
{
170+
(int newWidth, int newHeight, newDownscaleLevel) = CalculateScaledSize(newUnscaledWidth, newUnscaledHeight, newDownscaleLevel);
171+
172+
captureZone.UnscaledWidth = newUnscaledWidth;
173+
captureZone.UnscaledHeight = newUnscaledHeight;
174+
captureZone.Width = newWidth;
175+
captureZone.Height = newHeight;
176+
captureZone.DownscaleLevel = newDownscaleLevel;
177+
captureZone.Image.Resize(newWidth, newHeight);
178+
}
179+
}
180+
}
181+
182+
public virtual void Restart()
183+
{
184+
if (_isDisposed) throw new ObjectDisposedException(GetType().FullName);
185+
}
186+
187+
/// <inheritdoc />
188+
public void Dispose()
189+
{
190+
if (_isDisposed) return;
191+
192+
try
193+
{
194+
Dispose(true);
195+
}
196+
catch { /* don't throw in dispose! */ }
197+
198+
GC.SuppressFinalize(this);
199+
200+
_isDisposed = true;
201+
}
202+
203+
protected virtual void Dispose(bool disposing) { }
204+
205+
#endregion
206+
}

ScreenCapture.NET/IScreenCapture.cs renamed to ScreenCapture.NET/Generic/IScreenCapture.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ public interface IScreenCapture : IDisposable
3232
/// <param name="height">The height of the region to capture (must be &gt;= 0 and this + y must be &lt;= screen-height).</param>
3333
/// <param name="downscaleLevel">The level of downscaling applied to the image of this region before copying to local memory. The calculation is (width and height)/2^downscaleLevel.</param>
3434
/// <returns>The new <see cref="CaptureScreen"/>.</returns>
35-
CaptureZone RegisterCaptureZone(int x, int y, int width, int height, int downscaleLevel = 0);
35+
ICaptureZone RegisterCaptureZone(int x, int y, int width, int height, int downscaleLevel = 0);
3636

3737
/// <summary>
3838
/// Removes the given <see cref="CaptureScreen"/> from the <see cref="IScreenCapture"/>.
3939
/// </summary>
4040
/// <param name="captureZone">The previously registered <see cref="CaptureScreen"/>.</param>
4141
/// <returns><c>true</c> if the <see cref="CaptureScreen"/> was successfully removed; otherwise, <c>false</c>.</returns>
42-
bool UnregisterCaptureZone(CaptureZone captureZone);
42+
bool UnregisterCaptureZone(ICaptureZone captureZone);
4343

4444
/// <summary>
4545
/// Updates the the given <see cref="CaptureScreen"/>.
@@ -53,7 +53,7 @@ public interface IScreenCapture : IDisposable
5353
/// <param name="width">The width of the region to capture (must be &gt;= 0 and this + x must be &lt;= screen-width).</param>
5454
/// <param name="height">The new height of the region to capture (must be &gt;= 0 and this + y must be &lt;= screen-height).</param>
5555
/// <param name="downscaleLevel">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.</param>
56-
void UpdateCaptureZone(CaptureZone captureZone, int? x = null, int? y = null, int? width = null, int? height = null, int? downscaleLevel = null);
56+
void UpdateCaptureZone(ICaptureZone captureZone, int? x = null, int? y = null, int? width = null, int? height = null, int? downscaleLevel = null);
5757

5858
/// <summary>
5959
/// Restarts the <see cref="IScreenCapture"/>.
File renamed without changes.

ScreenCapture.NET/Model/BlackBarDetection.cs

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public sealed class BlackBarDetection
1111
{
1212
#region Properties & Fields
1313

14-
private readonly CaptureZone _captureZone;
14+
private readonly ICaptureZone _captureZone;
1515

1616
private int? _top;
1717
/// <summary>
@@ -55,7 +55,7 @@ public int Threshold
5555

5656
#region Constructors
5757

58-
internal BlackBarDetection(CaptureZone captureZone)
58+
internal BlackBarDetection(ICaptureZone captureZone)
5959
{
6060
this._captureZone = captureZone;
6161
}
@@ -77,62 +77,62 @@ public void InvalidateCache()
7777

7878
private int CalculateTop()
7979
{
80-
int threshold = Threshold;
81-
int stride = _captureZone.Stride;
82-
for (int row = 0; row < _captureZone.Height; row++)
83-
{
84-
Span<byte> data = new(_captureZone.Buffer, row * stride, stride);
85-
for (int i = 0; i < data.Length; i += 4)
86-
if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
87-
return row;
88-
}
80+
//int threshold = Threshold;
81+
//int stride = _captureZone.Stride;
82+
//for (int row = 0; row < _captureZone.Height; row++)
83+
//{
84+
// Span<byte> data = new(_captureZone.Buffer, row * stride, stride);
85+
// for (int i = 0; i < data.Length; i += 4)
86+
// if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
87+
// return row;
88+
//}
8989

9090
return 0;
9191
}
9292

9393
private int CalculateBottom()
9494
{
95-
int threshold = Threshold;
96-
int stride = _captureZone.Stride;
97-
for (int row = _captureZone.Height - 1; row >= 0; row--)
98-
{
99-
Span<byte> data = new(_captureZone.Buffer, row * stride, stride);
100-
for (int i = 0; i < data.Length; i += 4)
101-
if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
102-
return (_captureZone.Height - 1) - row;
103-
}
95+
//int threshold = Threshold;
96+
//int stride = _captureZone.Stride;
97+
//for (int row = _captureZone.Height - 1; row >= 0; row--)
98+
//{
99+
// Span<byte> data = new(_captureZone.Buffer, row * stride, stride);
100+
// for (int i = 0; i < data.Length; i += 4)
101+
// if ((data[i] > threshold) || (data[i + 1] > threshold) || (data[i + 2] > threshold))
102+
// return (_captureZone.Height - 1) - row;
103+
//}
104104

105105
return 0;
106106
}
107107

108108
private int CalculateLeft()
109109
{
110-
int threshold = Threshold;
111-
int stride = _captureZone.Stride;
112-
byte[] buffer = _captureZone.Buffer;
113-
for (int column = 0; column < _captureZone.Width; column++)
114-
for (int row = 0; row < _captureZone.Height; row++)
115-
{
116-
int offset = (stride * row) + (column * 4);
117-
if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
118-
return column;
119-
}
110+
//int threshold = Threshold;
111+
//int stride = _captureZone.Stride;
112+
//byte[] buffer = _captureZone.Buffer;
113+
//for (int column = 0; column < _captureZone.Width; column++)
114+
// for (int row = 0; row < _captureZone.Height; row++)
115+
// {
116+
// int offset = (stride * row) + (column * 4);
117+
// if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
118+
// return column;
119+
// }
120120

121121
return 0;
122122
}
123123

124124
private int CalculateRight()
125125
{
126-
int threshold = Threshold;
127-
int stride = _captureZone.Stride;
128-
byte[] buffer = _captureZone.Buffer;
129-
for (int column = _captureZone.Width - 1; column >= 0; column--)
130-
for (int row = 0; row < _captureZone.Height; row++)
131-
{
132-
int offset = (stride * row) + (column * 4);
133-
if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
134-
return (_captureZone.Width - 1) - column;
135-
}
126+
//int threshold = Threshold;
127+
//int stride = _captureZone.Stride;
128+
//byte[] buffer = _captureZone.Buffer;
129+
//for (int column = _captureZone.Width - 1; column >= 0; column--)
130+
// for (int row = 0; row < _captureZone.Height; row++)
131+
// {
132+
// int offset = (stride * row) + (column * 4);
133+
// if ((buffer[offset] > threshold) || (buffer[offset + 1] > threshold) || (buffer[offset + 2] > threshold))
134+
// return (_captureZone.Width - 1) - column;
135+
// }
136136

137137
return 0;
138138
}

0 commit comments

Comments
 (0)