Skip to content

Commit 9f9f153

Browse files
committed
Reworked result image (WIP)
1 parent 8ce7db1 commit 9f9f153

File tree

9 files changed

+672
-179
lines changed

9 files changed

+672
-179
lines changed
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
// DarthAffe 05.09.2023: Based on https://github.com/CommunityToolkit/dotnet/blob/b0d6c4f9c0cfb5d860400abb00b0ca1b3e94dfa4/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable%7BT%7D.cs
2+
3+
// Licensed to the .NET Foundation under one or more agreements.
4+
// The .NET Foundation licenses this file to you under the MIT license.
5+
// See the LICENSE file in the project root for more information.
6+
7+
using System;
8+
using System.Runtime.CompilerServices;
9+
using System.Runtime.InteropServices;
10+
11+
namespace ScreenCapture.NET;
12+
13+
/// <summary>
14+
/// A <see langword="ref"/> <see langword="struct"/> that iterates readonly items from arbitrary memory locations.
15+
/// </summary>
16+
/// <typeparam name="T">The type of items to enumerate.</typeparam>
17+
public readonly ref struct ReadOnlyRefEnumerable<T>
18+
{
19+
#region Properties & Fields
20+
21+
/// <summary>
22+
/// The <see cref="ReadOnlySpan{T}"/> instance pointing to the first item in the target memory area.
23+
/// </summary>
24+
/// <remarks>The <see cref="ReadOnlySpan{T}.Length"/> field maps to the total available length.</remarks>
25+
private readonly ReadOnlySpan<T> _span;
26+
27+
/// <summary>
28+
/// The distance between items in the sequence to enumerate.
29+
/// </summary>
30+
/// <remarks>The distance refers to <typeparamref name="T"/> items, not byte offset.</remarks>
31+
private readonly int _step;
32+
33+
/// <summary>
34+
/// Gets the total available length for the sequence.
35+
/// </summary>
36+
public int Length
37+
{
38+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
39+
get => _span.Length;
40+
}
41+
42+
/// <summary>
43+
/// Gets the element at the specified zero-based index.
44+
/// </summary>
45+
/// <param name="index">The zero-based index of the element.</param>
46+
/// <returns>A reference to the element at the specified index.</returns>
47+
/// <exception cref="IndexOutOfRangeException">
48+
/// Thrown when <paramref name="index"/> is invalid.
49+
/// </exception>
50+
public ref readonly T this[int index]
51+
{
52+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
53+
get
54+
{
55+
if ((uint)index >= (uint)Length) throw new IndexOutOfRangeException();
56+
57+
ref T r0 = ref MemoryMarshal.GetReference(_span);
58+
nint offset = (nint)(uint)index * (nint)(uint)_step;
59+
ref T ri = ref Unsafe.Add(ref r0, offset);
60+
61+
return ref ri;
62+
}
63+
}
64+
65+
/// <summary>
66+
/// Gets the element at the specified zero-based index.
67+
/// </summary>
68+
/// <param name="index">The zero-based index of the element.</param>
69+
/// <returns>A reference to the element at the specified index.</returns>
70+
/// <exception cref="IndexOutOfRangeException">
71+
/// Thrown when <paramref name="index"/> is invalid.
72+
/// </exception>
73+
public ref readonly T this[Index index]
74+
{
75+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
76+
get => ref this[index.GetOffset(Length)];
77+
}
78+
79+
#endregion
80+
81+
#region Constructors
82+
83+
/// <summary>
84+
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
85+
/// </summary>
86+
/// <param name="reference">A reference to the first item of the sequence.</param>
87+
/// <param name="length">The number of items in the sequence.</param>
88+
/// <param name="step">The distance between items in the sequence to enumerate.</param>
89+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
90+
internal ReadOnlyRefEnumerable(in T reference, int length, int step)
91+
{
92+
this._step = step;
93+
94+
_span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(reference), length);
95+
}
96+
97+
#endregion
98+
99+
#region Methods
100+
101+
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
102+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
103+
public Enumerator GetEnumerator() => new(_span, _step);
104+
105+
public T[] ToArray()
106+
{
107+
int length = _span.Length;
108+
109+
// Empty array if no data is mapped
110+
if (length == 0)
111+
return Array.Empty<T>();
112+
113+
T[] array = new T[length];
114+
CopyTo(array);
115+
116+
return array;
117+
}
118+
119+
/// <summary>
120+
/// Copies the contents of this <see cref="ReadOnlyRefEnumerable{T}"/> into a destination <see cref="Span{T}"/> instance.
121+
/// </summary>
122+
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
123+
/// <exception cref="ArgumentException">
124+
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="ReadOnlyRefEnumerable{T}"/> instance.
125+
/// </exception>
126+
public void CopyTo(Span<T> destination)
127+
{
128+
if (_step == 1)
129+
{
130+
_span.CopyTo(destination);
131+
return;
132+
}
133+
134+
ref T sourceRef = ref MemoryMarshal.GetReference(_span);
135+
int length = _span.Length;
136+
if ((uint)destination.Length < (uint)length)
137+
throw new ArgumentException("The target span is too short to copy all the current items to.");
138+
139+
ref T destinationRef = ref MemoryMarshal.GetReference(destination);
140+
141+
CopyTo(ref sourceRef, ref destinationRef, (nint)(uint)length, (nint)(uint)_step);
142+
}
143+
144+
/// <summary>
145+
/// Attempts to copy the current <see cref="ReadOnlyRefEnumerable{T}"/> instance to a destination <see cref="Span{T}"/>.
146+
/// </summary>
147+
/// <param name="destination">The target <see cref="Span{T}"/> of the copy operation.</param>
148+
/// <returns>Whether or not the operation was successful.</returns>
149+
public bool TryCopyTo(Span<T> destination)
150+
{
151+
if (destination.Length >= _span.Length)
152+
{
153+
CopyTo(destination);
154+
return true;
155+
}
156+
157+
return false;
158+
}
159+
160+
private static void CopyTo(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep)
161+
{
162+
nint sourceOffset = 0;
163+
nint destinationOffset = 0;
164+
165+
while (length >= 8)
166+
{
167+
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
168+
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
169+
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
170+
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
171+
Unsafe.Add(ref destinationRef, destinationOffset + 4) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
172+
Unsafe.Add(ref destinationRef, destinationOffset + 5) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
173+
Unsafe.Add(ref destinationRef, destinationOffset + 6) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
174+
Unsafe.Add(ref destinationRef, destinationOffset + 7) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
175+
176+
length -= 8;
177+
sourceOffset += sourceStep;
178+
destinationOffset += 8;
179+
}
180+
181+
if (length >= 4)
182+
{
183+
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
184+
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
185+
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
186+
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
187+
188+
length -= 4;
189+
sourceOffset += sourceStep;
190+
destinationOffset += 4;
191+
}
192+
193+
while (length > 0)
194+
{
195+
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
196+
197+
length -= 1;
198+
sourceOffset += sourceStep;
199+
destinationOffset += 1;
200+
}
201+
}
202+
203+
#endregion
204+
205+
/// <summary>
206+
/// A custom enumerator type to traverse items within a <see cref="ReadOnlyRefEnumerable{T}"/> instance.
207+
/// </summary>
208+
public ref struct Enumerator
209+
{
210+
#region Properties & Fields
211+
212+
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}._span"/>
213+
private readonly ReadOnlySpan<T> _span;
214+
215+
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}._step"/>
216+
private readonly int _step;
217+
218+
/// <summary>
219+
/// The current position in the sequence.
220+
/// </summary>
221+
private int _position;
222+
223+
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
224+
public readonly ref readonly T Current
225+
{
226+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
227+
get
228+
{
229+
ref T r0 = ref MemoryMarshal.GetReference(_span);
230+
231+
nint offset = (nint)(uint)_position * (nint)(uint)_step;
232+
ref T ri = ref Unsafe.Add(ref r0, offset);
233+
234+
return ref ri;
235+
}
236+
}
237+
238+
#endregion
239+
240+
#region Constructors
241+
242+
/// <summary>
243+
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
244+
/// </summary>
245+
/// <param name="span">The <see cref="ReadOnlySpan{T}"/> instance with the info on the items to traverse.</param>
246+
/// <param name="step">The distance between items in the sequence to enumerate.</param>
247+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
248+
internal Enumerator(ReadOnlySpan<T> span, int step)
249+
{
250+
this._span = span;
251+
this._step = step;
252+
253+
_position = -1;
254+
}
255+
256+
#endregion
257+
258+
#region Methods
259+
260+
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
261+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
262+
public bool MoveNext() => ++_position < _span.Length;
263+
264+
#endregion
265+
}
266+
}

ScreenCapture.NET/DirectX/DX11ScreenCapture.cs

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ protected override bool PerformScreenCapture()
133133
return result;
134134
}
135135

136-
protected override void PerformCaptureZoneUpdate(CaptureZone<ColorBGRA> captureZone)
136+
protected override void PerformCaptureZoneUpdate(CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
137137
{
138138
if (_context == null) return;
139139

@@ -157,25 +157,26 @@ protected override void PerformCaptureZoneUpdate(CaptureZone<ColorBGRA> captureZ
157157
textures.Y + textures.UnscaledHeight, 1));
158158

159159
MappedSubresource mapSource = _context.Map(textures.StagingTexture, 0, MapMode.Read, MapFlags.None);
160-
using IDisposable @lock = captureZone.Image.Lock();
160+
161+
using IDisposable @lock = captureZone.Lock();
161162
{
162-
Span<byte> source = mapSource.AsSpan(mapSource.RowPitch * textures.Height);
163+
ReadOnlySpan<byte> source = mapSource.AsSpan(mapSource.RowPitch * textures.Height);
163164
switch (Display.Rotation)
164165
{
165166
case Rotation.Rotation90:
166-
CopyRotate90(source, mapSource.RowPitch, captureZone);
167+
CopyRotate90(source, mapSource.RowPitch, captureZone, buffer);
167168
break;
168169

169170
case Rotation.Rotation180:
170-
CopyRotate180(source, mapSource.RowPitch, captureZone);
171+
CopyRotate180(source, mapSource.RowPitch, captureZone, buffer);
171172
break;
172173

173174
case Rotation.Rotation270:
174-
CopyRotate270(source, mapSource.RowPitch, captureZone);
175+
CopyRotate270(source, mapSource.RowPitch, captureZone, buffer);
175176
break;
176177

177178
default:
178-
CopyRotate0(source, mapSource.RowPitch, captureZone);
179+
CopyRotate0(source, mapSource.RowPitch, captureZone, buffer);
179180
break;
180181
}
181182
}
@@ -185,11 +186,11 @@ protected override void PerformCaptureZoneUpdate(CaptureZone<ColorBGRA> captureZ
185186
}
186187

187188
[MethodImpl(MethodImplOptions.AggressiveInlining)]
188-
private static void CopyRotate0(in Span<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone)
189+
private static void CopyRotate0(in ReadOnlySpan<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
189190
{
190-
int height = captureZone.Image.Height;
191-
int stride = captureZone.Image.Stride;
192-
Span<byte> target = captureZone.Image.Raw;
191+
int height = captureZone.Height;
192+
int stride = captureZone.Stride;
193+
Span<byte> target = buffer;
193194

194195
for (int y = 0; y < height; y++)
195196
{
@@ -201,51 +202,49 @@ private static void CopyRotate0(in Span<byte> source, int sourceStride, in Captu
201202
}
202203

203204
[MethodImpl(MethodImplOptions.AggressiveInlining)]
204-
private static void CopyRotate90(in Span<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone)
205+
private static void CopyRotate90(in ReadOnlySpan<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
205206
{
206-
int width = captureZone.Image.Width;
207-
int height = captureZone.Image.Height;
208-
int usedBytesPerLine = height * captureZone.Image.ColorFormat.BytesPerPixel;
209-
Span<ColorBGRA> target = captureZone.Image.Pixels;
207+
int width = captureZone.Width;
208+
int height = captureZone.Height;
209+
int usedBytesPerLine = height * captureZone.ColorFormat.BytesPerPixel;
210+
Span<ColorBGRA> target = MemoryMarshal.Cast<byte, ColorBGRA>(buffer);
210211

211212
for (int x = 0; x < width; x++)
212213
{
213-
Span<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(x * sourceStride, usedBytesPerLine));
214+
ReadOnlySpan<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(x * sourceStride, usedBytesPerLine));
214215
for (int y = 0; y < src.Length; y++)
215216
target[(y * width) + (width - x - 1)] = src[y];
216217
}
217218
}
218219

219220
[MethodImpl(MethodImplOptions.AggressiveInlining)]
220-
private static void CopyRotate180(in Span<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone)
221+
private static void CopyRotate180(in ReadOnlySpan<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
221222
{
222-
int width = captureZone.Image.Width;
223-
int height = captureZone.Image.Height;
224-
int bpp = captureZone.Image.ColorFormat.BytesPerPixel;
223+
int width = captureZone.Width;
224+
int height = captureZone.Height;
225+
int bpp = captureZone.ColorFormat.BytesPerPixel;
225226
int usedBytesPerLine = width * bpp;
226-
Span<ColorBGRA> target = captureZone.Image.Pixels;
227+
Span<ColorBGRA> target = MemoryMarshal.Cast<byte, ColorBGRA>(buffer);
227228

228229
for (int y = 0; y < height; y++)
229230
{
230-
Span<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(y * sourceStride, usedBytesPerLine));
231+
ReadOnlySpan<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(y * sourceStride, usedBytesPerLine));
231232
for (int x = 0; x < src.Length; x++)
232-
{
233233
target[((height - y - 1) * width) + (width - x - 1)] = src[x];
234-
}
235234
}
236235
}
237236

238237
[MethodImpl(MethodImplOptions.AggressiveInlining)]
239-
private static void CopyRotate270(in Span<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone)
238+
private static void CopyRotate270(in ReadOnlySpan<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
240239
{
241-
int width = captureZone.Image.Width;
242-
int height = captureZone.Image.Height;
243-
int usedBytesPerLine = height * captureZone.Image.ColorFormat.BytesPerPixel;
244-
Span<ColorBGRA> target = captureZone.Image.Pixels;
240+
int width = captureZone.Width;
241+
int height = captureZone.Height;
242+
int usedBytesPerLine = height * captureZone.ColorFormat.BytesPerPixel;
243+
Span<ColorBGRA> target = MemoryMarshal.Cast<byte, ColorBGRA>(buffer);
245244

246245
for (int x = 0; x < width; x++)
247246
{
248-
Span<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(x * sourceStride, usedBytesPerLine));
247+
ReadOnlySpan<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(x * sourceStride, usedBytesPerLine));
249248
for (int y = 0; y < src.Length; y++)
250249
target[((height - y - 1) * width) + x] = src[y];
251250
}

0 commit comments

Comments
 (0)