mirror of
https://github.com/DarthAffe/ScreenCapture.NET.git
synced 2025-12-13 05:48:39 +00:00
Reworked result image (WIP)
This commit is contained in:
parent
8ce7db15e8
commit
9f9f153da5
@ -0,0 +1,266 @@
|
|||||||
|
// DarthAffe 05.09.2023: Based on https://github.com/CommunityToolkit/dotnet/blob/b0d6c4f9c0cfb5d860400abb00b0ca1b3e94dfa4/src/CommunityToolkit.HighPerformance/Enumerables/ReadOnlyRefEnumerable%7BT%7D.cs
|
||||||
|
|
||||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace ScreenCapture.NET;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see langword="ref"/> <see langword="struct"/> that iterates readonly items from arbitrary memory locations.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of items to enumerate.</typeparam>
|
||||||
|
public readonly ref struct ReadOnlyRefEnumerable<T>
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="ReadOnlySpan{T}"/> instance pointing to the first item in the target memory area.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The <see cref="ReadOnlySpan{T}.Length"/> field maps to the total available length.</remarks>
|
||||||
|
private readonly ReadOnlySpan<T> _span;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The distance between items in the sequence to enumerate.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The distance refers to <typeparamref name="T"/> items, not byte offset.</remarks>
|
||||||
|
private readonly int _step;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the total available length for the sequence.
|
||||||
|
/// </summary>
|
||||||
|
public int Length
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _span.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the element at the specified zero-based index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The zero-based index of the element.</param>
|
||||||
|
/// <returns>A reference to the element at the specified index.</returns>
|
||||||
|
/// <exception cref="IndexOutOfRangeException">
|
||||||
|
/// Thrown when <paramref name="index"/> is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public ref readonly T this[int index]
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((uint)index >= (uint)Length) throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
ref T r0 = ref MemoryMarshal.GetReference(_span);
|
||||||
|
nint offset = (nint)(uint)index * (nint)(uint)_step;
|
||||||
|
ref T ri = ref Unsafe.Add(ref r0, offset);
|
||||||
|
|
||||||
|
return ref ri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the element at the specified zero-based index.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">The zero-based index of the element.</param>
|
||||||
|
/// <returns>A reference to the element at the specified index.</returns>
|
||||||
|
/// <exception cref="IndexOutOfRangeException">
|
||||||
|
/// Thrown when <paramref name="index"/> is invalid.
|
||||||
|
/// </exception>
|
||||||
|
public ref readonly T this[Index index]
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => ref this[index.GetOffset(Length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ReadOnlyRefEnumerable{T}"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reference">A reference to the first item of the sequence.</param>
|
||||||
|
/// <param name="length">The number of items in the sequence.</param>
|
||||||
|
/// <param name="step">The distance between items in the sequence to enumerate.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal ReadOnlyRefEnumerable(in T reference, int length, int step)
|
||||||
|
{
|
||||||
|
this._step = step;
|
||||||
|
|
||||||
|
_span = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(reference), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Enumerator GetEnumerator() => new(_span, _step);
|
||||||
|
|
||||||
|
public T[] ToArray()
|
||||||
|
{
|
||||||
|
int length = _span.Length;
|
||||||
|
|
||||||
|
// Empty array if no data is mapped
|
||||||
|
if (length == 0)
|
||||||
|
return Array.Empty<T>();
|
||||||
|
|
||||||
|
T[] array = new T[length];
|
||||||
|
CopyTo(array);
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies the contents of this <see cref="ReadOnlyRefEnumerable{T}"/> into a destination <see cref="Span{T}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destination">The destination <see cref="Span{T}"/> instance.</param>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// Thrown when <paramref name="destination"/> is shorter than the source <see cref="ReadOnlyRefEnumerable{T}"/> instance.
|
||||||
|
/// </exception>
|
||||||
|
public void CopyTo(Span<T> destination)
|
||||||
|
{
|
||||||
|
if (_step == 1)
|
||||||
|
{
|
||||||
|
_span.CopyTo(destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref T sourceRef = ref MemoryMarshal.GetReference(_span);
|
||||||
|
int length = _span.Length;
|
||||||
|
if ((uint)destination.Length < (uint)length)
|
||||||
|
throw new ArgumentException("The target span is too short to copy all the current items to.");
|
||||||
|
|
||||||
|
ref T destinationRef = ref MemoryMarshal.GetReference(destination);
|
||||||
|
|
||||||
|
CopyTo(ref sourceRef, ref destinationRef, (nint)(uint)length, (nint)(uint)_step);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to copy the current <see cref="ReadOnlyRefEnumerable{T}"/> instance to a destination <see cref="Span{T}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destination">The target <see cref="Span{T}"/> of the copy operation.</param>
|
||||||
|
/// <returns>Whether or not the operation was successful.</returns>
|
||||||
|
public bool TryCopyTo(Span<T> destination)
|
||||||
|
{
|
||||||
|
if (destination.Length >= _span.Length)
|
||||||
|
{
|
||||||
|
CopyTo(destination);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CopyTo(ref T sourceRef, ref T destinationRef, nint length, nint sourceStep)
|
||||||
|
{
|
||||||
|
nint sourceOffset = 0;
|
||||||
|
nint destinationOffset = 0;
|
||||||
|
|
||||||
|
while (length >= 8)
|
||||||
|
{
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 4) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 5) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 6) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 7) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
|
||||||
|
length -= 8;
|
||||||
|
sourceOffset += sourceStep;
|
||||||
|
destinationOffset += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length >= 4)
|
||||||
|
{
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 0) = Unsafe.Add(ref sourceRef, sourceOffset);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 1) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 2) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset + 3) = Unsafe.Add(ref sourceRef, sourceOffset += sourceStep);
|
||||||
|
|
||||||
|
length -= 4;
|
||||||
|
sourceOffset += sourceStep;
|
||||||
|
destinationOffset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (length > 0)
|
||||||
|
{
|
||||||
|
Unsafe.Add(ref destinationRef, destinationOffset) = Unsafe.Add(ref sourceRef, sourceOffset);
|
||||||
|
|
||||||
|
length -= 1;
|
||||||
|
sourceOffset += sourceStep;
|
||||||
|
destinationOffset += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A custom enumerator type to traverse items within a <see cref="ReadOnlyRefEnumerable{T}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
public ref struct Enumerator
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}._span"/>
|
||||||
|
private readonly ReadOnlySpan<T> _span;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ReadOnlyRefEnumerable{T}._step"/>
|
||||||
|
private readonly int _step;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The current position in the sequence.
|
||||||
|
/// </summary>
|
||||||
|
private int _position;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
|
||||||
|
public readonly ref readonly T Current
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ref T r0 = ref MemoryMarshal.GetReference(_span);
|
||||||
|
|
||||||
|
nint offset = (nint)(uint)_position * (nint)(uint)_step;
|
||||||
|
ref T ri = ref Unsafe.Add(ref r0, offset);
|
||||||
|
|
||||||
|
return ref ri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="span">The <see cref="ReadOnlySpan{T}"/> instance with the info on the items to traverse.</param>
|
||||||
|
/// <param name="step">The distance between items in the sequence to enumerate.</param>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal Enumerator(ReadOnlySpan<T> span, int step)
|
||||||
|
{
|
||||||
|
this._span = span;
|
||||||
|
this._step = step;
|
||||||
|
|
||||||
|
_position = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool MoveNext() => ++_position < _span.Length;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -133,7 +133,7 @@ public sealed class DX11ScreenCapture : AbstractScreenCapture<ColorBGRA>
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PerformCaptureZoneUpdate(CaptureZone<ColorBGRA> captureZone)
|
protected override void PerformCaptureZoneUpdate(CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
|
||||||
{
|
{
|
||||||
if (_context == null) return;
|
if (_context == null) return;
|
||||||
|
|
||||||
@ -157,25 +157,26 @@ public sealed class DX11ScreenCapture : AbstractScreenCapture<ColorBGRA>
|
|||||||
textures.Y + textures.UnscaledHeight, 1));
|
textures.Y + textures.UnscaledHeight, 1));
|
||||||
|
|
||||||
MappedSubresource mapSource = _context.Map(textures.StagingTexture, 0, MapMode.Read, MapFlags.None);
|
MappedSubresource mapSource = _context.Map(textures.StagingTexture, 0, MapMode.Read, MapFlags.None);
|
||||||
using IDisposable @lock = captureZone.Image.Lock();
|
|
||||||
|
using IDisposable @lock = captureZone.Lock();
|
||||||
{
|
{
|
||||||
Span<byte> source = mapSource.AsSpan(mapSource.RowPitch * textures.Height);
|
ReadOnlySpan<byte> source = mapSource.AsSpan(mapSource.RowPitch * textures.Height);
|
||||||
switch (Display.Rotation)
|
switch (Display.Rotation)
|
||||||
{
|
{
|
||||||
case Rotation.Rotation90:
|
case Rotation.Rotation90:
|
||||||
CopyRotate90(source, mapSource.RowPitch, captureZone);
|
CopyRotate90(source, mapSource.RowPitch, captureZone, buffer);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Rotation.Rotation180:
|
case Rotation.Rotation180:
|
||||||
CopyRotate180(source, mapSource.RowPitch, captureZone);
|
CopyRotate180(source, mapSource.RowPitch, captureZone, buffer);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Rotation.Rotation270:
|
case Rotation.Rotation270:
|
||||||
CopyRotate270(source, mapSource.RowPitch, captureZone);
|
CopyRotate270(source, mapSource.RowPitch, captureZone, buffer);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
CopyRotate0(source, mapSource.RowPitch, captureZone);
|
CopyRotate0(source, mapSource.RowPitch, captureZone, buffer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -185,11 +186,11 @@ public sealed class DX11ScreenCapture : AbstractScreenCapture<ColorBGRA>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void CopyRotate0(in Span<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone)
|
private static void CopyRotate0(in ReadOnlySpan<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
|
||||||
{
|
{
|
||||||
int height = captureZone.Image.Height;
|
int height = captureZone.Height;
|
||||||
int stride = captureZone.Image.Stride;
|
int stride = captureZone.Stride;
|
||||||
Span<byte> target = captureZone.Image.Raw;
|
Span<byte> target = buffer;
|
||||||
|
|
||||||
for (int y = 0; y < height; y++)
|
for (int y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
@ -201,51 +202,49 @@ public sealed class DX11ScreenCapture : AbstractScreenCapture<ColorBGRA>
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void CopyRotate90(in Span<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone)
|
private static void CopyRotate90(in ReadOnlySpan<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
|
||||||
{
|
{
|
||||||
int width = captureZone.Image.Width;
|
int width = captureZone.Width;
|
||||||
int height = captureZone.Image.Height;
|
int height = captureZone.Height;
|
||||||
int usedBytesPerLine = height * captureZone.Image.ColorFormat.BytesPerPixel;
|
int usedBytesPerLine = height * captureZone.ColorFormat.BytesPerPixel;
|
||||||
Span<ColorBGRA> target = captureZone.Image.Pixels;
|
Span<ColorBGRA> target = MemoryMarshal.Cast<byte, ColorBGRA>(buffer);
|
||||||
|
|
||||||
for (int x = 0; x < width; x++)
|
for (int x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
Span<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(x * sourceStride, usedBytesPerLine));
|
ReadOnlySpan<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(x * sourceStride, usedBytesPerLine));
|
||||||
for (int y = 0; y < src.Length; y++)
|
for (int y = 0; y < src.Length; y++)
|
||||||
target[(y * width) + (width - x - 1)] = src[y];
|
target[(y * width) + (width - x - 1)] = src[y];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void CopyRotate180(in Span<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone)
|
private static void CopyRotate180(in ReadOnlySpan<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
|
||||||
{
|
{
|
||||||
int width = captureZone.Image.Width;
|
int width = captureZone.Width;
|
||||||
int height = captureZone.Image.Height;
|
int height = captureZone.Height;
|
||||||
int bpp = captureZone.Image.ColorFormat.BytesPerPixel;
|
int bpp = captureZone.ColorFormat.BytesPerPixel;
|
||||||
int usedBytesPerLine = width * bpp;
|
int usedBytesPerLine = width * bpp;
|
||||||
Span<ColorBGRA> target = captureZone.Image.Pixels;
|
Span<ColorBGRA> target = MemoryMarshal.Cast<byte, ColorBGRA>(buffer);
|
||||||
|
|
||||||
for (int y = 0; y < height; y++)
|
for (int y = 0; y < height; y++)
|
||||||
{
|
{
|
||||||
Span<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(y * sourceStride, usedBytesPerLine));
|
ReadOnlySpan<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(y * sourceStride, usedBytesPerLine));
|
||||||
for (int x = 0; x < src.Length; x++)
|
for (int x = 0; x < src.Length; x++)
|
||||||
{
|
|
||||||
target[((height - y - 1) * width) + (width - x - 1)] = src[x];
|
target[((height - y - 1) * width) + (width - x - 1)] = src[x];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static void CopyRotate270(in Span<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone)
|
private static void CopyRotate270(in ReadOnlySpan<byte> source, int sourceStride, in CaptureZone<ColorBGRA> captureZone, in Span<byte> buffer)
|
||||||
{
|
{
|
||||||
int width = captureZone.Image.Width;
|
int width = captureZone.Width;
|
||||||
int height = captureZone.Image.Height;
|
int height = captureZone.Height;
|
||||||
int usedBytesPerLine = height * captureZone.Image.ColorFormat.BytesPerPixel;
|
int usedBytesPerLine = height * captureZone.ColorFormat.BytesPerPixel;
|
||||||
Span<ColorBGRA> target = captureZone.Image.Pixels;
|
Span<ColorBGRA> target = MemoryMarshal.Cast<byte, ColorBGRA>(buffer);
|
||||||
|
|
||||||
for (int x = 0; x < width; x++)
|
for (int x = 0; x < width; x++)
|
||||||
{
|
{
|
||||||
Span<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(x * sourceStride, usedBytesPerLine));
|
ReadOnlySpan<ColorBGRA> src = MemoryMarshal.Cast<byte, ColorBGRA>(source.Slice(x * sourceStride, usedBytesPerLine));
|
||||||
for (int y = 0; y < src.Length; y++)
|
for (int y = 0; y < src.Length; y++)
|
||||||
target[((height - y - 1) * width) + x] = src[y];
|
target[((height - y - 1) * width) + x] = src[y];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,7 @@ public abstract class AbstractScreenCapture<TColor> : IScreenCapture
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PerformCaptureZoneUpdate(captureZone);
|
PerformCaptureZoneUpdate(captureZone, captureZone.InternalBuffer);
|
||||||
captureZone.SetUpdated();
|
captureZone.SetUpdated();
|
||||||
}
|
}
|
||||||
catch { /* */ }
|
catch { /* */ }
|
||||||
@ -69,7 +69,7 @@ public abstract class AbstractScreenCapture<TColor> : IScreenCapture
|
|||||||
|
|
||||||
protected abstract bool PerformScreenCapture();
|
protected abstract bool PerformScreenCapture();
|
||||||
|
|
||||||
protected abstract void PerformCaptureZoneUpdate(CaptureZone<TColor> captureZone);
|
protected abstract void PerformCaptureZoneUpdate(CaptureZone<TColor> captureZone, in Span<byte> buffer);
|
||||||
|
|
||||||
protected virtual void OnUpdated(bool result)
|
protected virtual void OnUpdated(bool result)
|
||||||
{
|
{
|
||||||
@ -93,11 +93,7 @@ public abstract class AbstractScreenCapture<TColor> : IScreenCapture
|
|||||||
int unscaledHeight = height;
|
int unscaledHeight = height;
|
||||||
(width, height, downscaleLevel) = CalculateScaledSize(unscaledWidth, unscaledHeight, downscaleLevel);
|
(width, height, downscaleLevel) = CalculateScaledSize(unscaledWidth, unscaledHeight, downscaleLevel);
|
||||||
|
|
||||||
#if NET7_0_OR_GREATER
|
CaptureZone<TColor> captureZone = new(_indexCounter++, Display, x, y, width, height, downscaleLevel, unscaledWidth, unscaledHeight);
|
||||||
CaptureZone<TColor> captureZone = new(_indexCounter++, Display, x, y, width, height, downscaleLevel, unscaledWidth, unscaledHeight, new ScreenImage<TColor>(width, height, TColor.ColorFormat));
|
|
||||||
#else
|
|
||||||
CaptureZone<TColor> captureZone = new(_indexCounter++, Display, x, y, width, height, downscaleLevel, unscaledWidth, unscaledHeight, new ScreenImage<TColor>(width, height, IColor.GetColorFormat<TColor>()));
|
|
||||||
#endif
|
|
||||||
CaptureZones.Add(captureZone);
|
CaptureZones.Add(captureZone);
|
||||||
|
|
||||||
return captureZone;
|
return captureZone;
|
||||||
@ -168,13 +164,7 @@ public abstract class AbstractScreenCapture<TColor> : IScreenCapture
|
|||||||
if ((width != null) || (height != null) || (downscaleLevel != null))
|
if ((width != null) || (height != null) || (downscaleLevel != null))
|
||||||
{
|
{
|
||||||
(int newWidth, int newHeight, newDownscaleLevel) = CalculateScaledSize(newUnscaledWidth, newUnscaledHeight, newDownscaleLevel);
|
(int newWidth, int newHeight, newDownscaleLevel) = CalculateScaledSize(newUnscaledWidth, newUnscaledHeight, newDownscaleLevel);
|
||||||
|
captureZone.Resize(newWidth, newHeight, newDownscaleLevel, newUnscaledWidth, newUnscaledHeight);
|
||||||
captureZone.UnscaledWidth = newUnscaledWidth;
|
|
||||||
captureZone.UnscaledHeight = newUnscaledHeight;
|
|
||||||
captureZone.Width = newWidth;
|
|
||||||
captureZone.Height = newHeight;
|
|
||||||
captureZone.DownscaleLevel = newDownscaleLevel;
|
|
||||||
captureZone.Image.Resize(newWidth, newHeight);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
// ReSharper disable MemberCanBePrivate.Global
|
// ReSharper disable MemberCanBePrivate.Global
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace ScreenCapture.NET;
|
namespace ScreenCapture.NET;
|
||||||
|
|
||||||
@ -12,6 +15,8 @@ public sealed class CaptureZone<TColor> : ICaptureZone
|
|||||||
{
|
{
|
||||||
#region Properties & Fields
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private readonly object _lock = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the unique id of this <see cref="CaptureZone{T}"/>.
|
/// Gets the unique id of this <see cref="CaptureZone{T}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -19,6 +24,20 @@ public sealed class CaptureZone<TColor> : ICaptureZone
|
|||||||
|
|
||||||
public Display Display { get; }
|
public Display Display { get; }
|
||||||
|
|
||||||
|
#if NET7_0_OR_GREATER
|
||||||
|
public ColorFormat ColorFormat
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => TColor.ColorFormat;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
public ColorFormat ColorFormat
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => IColor.GetColorFormat<TColor>();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the x-location of the region on the screen.
|
/// Gets the x-location of the region on the screen.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -32,30 +51,53 @@ public sealed class CaptureZone<TColor> : ICaptureZone
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the width of the captured region.
|
/// Gets the width of the captured region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Width { get; internal set; }
|
public int Width { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the height of the captured region.
|
/// Gets the height of the captured region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Height { get; internal set; }
|
public int Height { get; private set; }
|
||||||
|
|
||||||
|
public int Stride
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => Width * ColorFormat.BytesPerPixel;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int DownscaleLevel { get; internal set; }
|
public int DownscaleLevel { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the original width of the region (this equals <see cref="Width"/> if <see cref="DownscaleLevel"/> is 0).
|
/// Gets the original width of the region (this equals <see cref="Width"/> if <see cref="DownscaleLevel"/> is 0).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int UnscaledWidth { get; internal set; }
|
public int UnscaledWidth { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the original height of the region (this equals <see cref="Height"/> if <see cref="DownscaleLevel"/> is 0).
|
/// Gets the original height of the region (this equals <see cref="Height"/> if <see cref="DownscaleLevel"/> is 0).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int UnscaledHeight { get; internal set; }
|
public int UnscaledHeight { get; private set; }
|
||||||
|
|
||||||
IScreenImage ICaptureZone.Image => Image;
|
internal byte[] InternalBuffer { get; set; }
|
||||||
public ScreenImage<TColor> Image { get; }
|
|
||||||
|
public ReadOnlySpan<byte> RawBuffer
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => InternalBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlySpan<TColor> Pixels
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => MemoryMarshal.Cast<byte, TColor>(RawBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Image<TColor> Image
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => new(Pixels, 0, 0, Width, Height, Width);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets if the <see cref="CaptureZone{T}"/> should be automatically updated on every captured frame.
|
/// Gets or sets if the <see cref="CaptureZone{T}"/> should be automatically updated on every captured frame.
|
||||||
@ -93,7 +135,7 @@ public sealed class CaptureZone<TColor> : ICaptureZone
|
|||||||
/// <param name="unscaledWidth">The original width of the region.</param>
|
/// <param name="unscaledWidth">The original width of the region.</param>
|
||||||
/// <param name="unscaledHeight">The original height of the region</param>
|
/// <param name="unscaledHeight">The original height of the region</param>
|
||||||
/// <param name="buffer">The buffer containing the image data.</param>
|
/// <param name="buffer">The buffer containing the image data.</param>
|
||||||
internal CaptureZone(int id, Display display, int x, int y, int width, int height, int downscaleLevel, int unscaledWidth, int unscaledHeight, ScreenImage<TColor> image)
|
internal CaptureZone(int id, Display display, int x, int y, int width, int height, int downscaleLevel, int unscaledWidth, int unscaledHeight)
|
||||||
{
|
{
|
||||||
this.Id = id;
|
this.Id = id;
|
||||||
this.Display = display;
|
this.Display = display;
|
||||||
@ -101,16 +143,23 @@ public sealed class CaptureZone<TColor> : ICaptureZone
|
|||||||
this.Y = y;
|
this.Y = y;
|
||||||
this.Width = width;
|
this.Width = width;
|
||||||
this.Height = height;
|
this.Height = height;
|
||||||
|
this.DownscaleLevel = downscaleLevel;
|
||||||
this.UnscaledWidth = unscaledWidth;
|
this.UnscaledWidth = unscaledWidth;
|
||||||
this.UnscaledHeight = unscaledHeight;
|
this.UnscaledHeight = unscaledHeight;
|
||||||
this.DownscaleLevel = downscaleLevel;
|
|
||||||
this.Image = image;
|
InternalBuffer = new byte[Stride * Height];
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
|
public IDisposable Lock()
|
||||||
|
{
|
||||||
|
Monitor.Enter(_lock);
|
||||||
|
return new UnlockDisposable(_lock);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Requests to update this <see cref="CaptureZone{T}"/> when the next frame is captured.
|
/// Requests to update this <see cref="CaptureZone{T}"/> when the next frame is captured.
|
||||||
/// Only necessary if <see cref="AutoUpdate"/> is set to <c>false</c>.
|
/// Only necessary if <see cref="AutoUpdate"/> is set to <c>false</c>.
|
||||||
@ -127,6 +176,19 @@ public sealed class CaptureZone<TColor> : ICaptureZone
|
|||||||
Updated?.Invoke(this, EventArgs.Empty);
|
Updated?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void Resize(int width, int height, int downscaleLevel, int unscaledWidth, int unscaledHeight)
|
||||||
|
{
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
DownscaleLevel = downscaleLevel;
|
||||||
|
UnscaledWidth = unscaledWidth;
|
||||||
|
UnscaledHeight = unscaledHeight;
|
||||||
|
|
||||||
|
int newBufferSize = Stride * Height;
|
||||||
|
if (newBufferSize != InternalBuffer.Length)
|
||||||
|
InternalBuffer = new byte[newBufferSize];
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether this <see cref="CaptureZone{T}"/> equals the given one.
|
/// Determines whether this <see cref="CaptureZone{T}"/> equals the given one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -141,4 +203,32 @@ public sealed class CaptureZone<TColor> : ICaptureZone
|
|||||||
public override int GetHashCode() => Id;
|
public override int GetHashCode() => Id;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private class UnlockDisposable : IDisposable
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private bool _disposed = false;
|
||||||
|
private readonly object _lock;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public UnlockDisposable(object @lock) => this._lock = @lock;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed) throw new ObjectDisposedException("The lock is already released");
|
||||||
|
|
||||||
|
Monitor.Exit(_lock);
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -38,7 +38,8 @@ public interface ICaptureZone
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
int UnscaledHeight { get; }
|
int UnscaledHeight { get; }
|
||||||
|
|
||||||
IScreenImage Image { get; }
|
ReadOnlySpan<byte> RawBuffer { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets if the <see cref="ICaptureZone"/> should be automatically updated on every captured frame.
|
/// Gets or sets if the <see cref="ICaptureZone"/> should be automatically updated on every captured frame.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace ScreenCapture.NET;
|
|
||||||
|
|
||||||
public interface IScreenImage
|
|
||||||
{
|
|
||||||
Span<byte> Raw { get; }
|
|
||||||
int Width { get; }
|
|
||||||
int Height { get; }
|
|
||||||
int Stride { get; }
|
|
||||||
ColorFormat ColorFormat { get; }
|
|
||||||
|
|
||||||
IDisposable Lock();
|
|
||||||
|
|
||||||
IColor this[int x, int y] { get; }
|
|
||||||
}
|
|
||||||
269
ScreenCapture.NET/Model/Image.cs
Normal file
269
ScreenCapture.NET/Model/Image.cs
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace ScreenCapture.NET;
|
||||||
|
|
||||||
|
public readonly ref struct Image<TColor>
|
||||||
|
where TColor : struct, IColor
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private readonly ReadOnlySpan<TColor> _pixels;
|
||||||
|
|
||||||
|
private readonly int _x;
|
||||||
|
private readonly int _y;
|
||||||
|
private readonly int _stride;
|
||||||
|
|
||||||
|
public int Width { get; }
|
||||||
|
public int Height { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Indexer
|
||||||
|
|
||||||
|
public TColor this[int x, int y]
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((x < 0) || (y < 0) || (x > Width) || (y > Height)) throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
return _pixels[((_y + y) * _stride) + (_x + x)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Image<TColor> this[int x, int y, int width, int height]
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((x < 0) || (y < 0) || ((x + width) > Width) || ((y + height) > Height)) throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
return new Image<TColor>(_pixels, _x + x, _y + y, width, height, _stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageRows Rows => new(_pixels, _x, _y, Width, Height, _stride);
|
||||||
|
public ImageColumns Columns => new(_pixels, _x, _y, Width, Height, _stride);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
internal Image(ReadOnlySpan<TColor> pixels, int x, int y, int width, int height, int stride)
|
||||||
|
{
|
||||||
|
this._pixels = pixels;
|
||||||
|
this._x = x;
|
||||||
|
this._y = y;
|
||||||
|
this.Width = width;
|
||||||
|
this.Height = height;
|
||||||
|
this._stride = stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
public void CopyTo(in Span<TColor> dest)
|
||||||
|
{
|
||||||
|
if (dest == null) throw new ArgumentNullException(nameof(dest));
|
||||||
|
if (dest.Length < (Width * Height)) throw new ArgumentException("The destination is too small to fit this image.", nameof(dest));
|
||||||
|
|
||||||
|
ImageRows rows = Rows;
|
||||||
|
Span<TColor> target = dest;
|
||||||
|
foreach (ReadOnlyRefEnumerable<TColor> row in rows)
|
||||||
|
{
|
||||||
|
row.CopyTo(target);
|
||||||
|
target = target[Width..];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Indexer-Structs
|
||||||
|
|
||||||
|
public readonly ref struct ImageRows
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private readonly ReadOnlySpan<TColor> _pixels;
|
||||||
|
private readonly int _x;
|
||||||
|
private readonly int _y;
|
||||||
|
private readonly int _width;
|
||||||
|
private readonly int _height;
|
||||||
|
private readonly int _stride;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Indexer
|
||||||
|
|
||||||
|
public readonly ReadOnlyRefEnumerable<TColor> this[int row]
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((row < 0) || (row > _height)) throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
ref TColor r0 = ref MemoryMarshal.GetReference(_pixels);
|
||||||
|
ref TColor rr = ref Unsafe.Add(ref r0, (nint)(uint)(((row + _y) * _stride) + _x));
|
||||||
|
|
||||||
|
return new ReadOnlyRefEnumerable<TColor>(rr, _width, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public ImageRows(ReadOnlySpan<TColor> pixels, int x, int y, int width, int height, int stride)
|
||||||
|
{
|
||||||
|
this._pixels = pixels;
|
||||||
|
this._x = x;
|
||||||
|
this._y = y;
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
this._stride = stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Enumerator GetEnumerator() => new(this);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public ref struct Enumerator
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private readonly ImageRows _rows;
|
||||||
|
private int _position;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
|
||||||
|
public ReadOnlyRefEnumerable<TColor> Current
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _rows[_position];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal Enumerator(ImageRows rows)
|
||||||
|
{
|
||||||
|
this._rows = rows;
|
||||||
|
|
||||||
|
_position = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool MoveNext() => ++_position < _rows._height;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly ref struct ImageColumns
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private readonly ReadOnlySpan<TColor> _pixels;
|
||||||
|
private readonly int _x;
|
||||||
|
private readonly int _y;
|
||||||
|
private readonly int _width;
|
||||||
|
private readonly int _height;
|
||||||
|
private readonly int _stride;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Indexer
|
||||||
|
|
||||||
|
public ReadOnlyRefEnumerable<TColor> this[int column]
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((column < 0) || (column > _width)) throw new IndexOutOfRangeException();
|
||||||
|
|
||||||
|
ref TColor r0 = ref MemoryMarshal.GetReference(_pixels);
|
||||||
|
ref TColor rc = ref Unsafe.Add(ref r0, (nint)(uint)((_y * _stride) + (column + _x)));
|
||||||
|
|
||||||
|
return new ReadOnlyRefEnumerable<TColor>(rc, _height, _stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
public ImageColumns(ReadOnlySpan<TColor> pixels, int x, int y, int width, int height, int stride)
|
||||||
|
{
|
||||||
|
this._pixels = pixels;
|
||||||
|
this._x = x;
|
||||||
|
this._y = y;
|
||||||
|
this._width = width;
|
||||||
|
this._height = height;
|
||||||
|
this._stride = stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public Enumerator GetEnumerator() => new(this);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public ref struct Enumerator
|
||||||
|
{
|
||||||
|
#region Properties & Fields
|
||||||
|
|
||||||
|
private readonly ImageColumns _columns;
|
||||||
|
private int _position;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="System.Collections.Generic.IEnumerator{T}.Current"/>
|
||||||
|
public ReadOnlyRefEnumerable<TColor> Current
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get => _columns[_position];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constructors
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
internal Enumerator(ImageColumns columns)
|
||||||
|
{
|
||||||
|
this._columns = columns;
|
||||||
|
this._position = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Methods
|
||||||
|
|
||||||
|
/// <inheritdoc cref="System.Collections.IEnumerator.MoveNext"/>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool MoveNext() => ++_position < _columns._width;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@ -1,102 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace ScreenCapture.NET;
|
|
||||||
|
|
||||||
public sealed class ScreenImage<TColor> : IScreenImage
|
|
||||||
where TColor : struct, IColor
|
|
||||||
{
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
private readonly object _lock = new();
|
|
||||||
private byte[] _buffer;
|
|
||||||
|
|
||||||
public Span<byte> Raw => _buffer;
|
|
||||||
public Span<TColor> Pixels => MemoryMarshal.Cast<byte, TColor>(_buffer);
|
|
||||||
|
|
||||||
public int Width { get; private set; }
|
|
||||||
public int Height { get; private set; }
|
|
||||||
public int Stride => Width * ColorFormat.BytesPerPixel;
|
|
||||||
public ColorFormat ColorFormat { get; }
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Indexer
|
|
||||||
|
|
||||||
IColor IScreenImage.this[int x, int y] => this[x, y];
|
|
||||||
public TColor this[int x, int y] => Pixels[(y * Width) + x];
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
internal ScreenImage(int width, int height, ColorFormat colorFormat)
|
|
||||||
{
|
|
||||||
this.Width = width;
|
|
||||||
this.Height = height;
|
|
||||||
this.ColorFormat = colorFormat;
|
|
||||||
|
|
||||||
_buffer = new byte[width * height * colorFormat.BytesPerPixel];
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
internal void Resize(int width, int height)
|
|
||||||
{
|
|
||||||
Width = width;
|
|
||||||
Height = height;
|
|
||||||
|
|
||||||
_buffer = new byte[width * height * ColorFormat.BytesPerPixel];
|
|
||||||
}
|
|
||||||
|
|
||||||
public IDisposable Lock()
|
|
||||||
{
|
|
||||||
Monitor.Enter(_lock);
|
|
||||||
return new UnlockDisposable(_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private class UnlockDisposable : IDisposable
|
|
||||||
{
|
|
||||||
#region Properties & Fields
|
|
||||||
|
|
||||||
private bool _disposed = false;
|
|
||||||
private readonly object _lock;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Constructors
|
|
||||||
|
|
||||||
public UnlockDisposable(object @lock) => this._lock = @lock;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Methods
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed) throw new ObjectDisposedException("The lock is already released");
|
|
||||||
|
|
||||||
Monitor.Exit(_lock);
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly ref struct ScreemImageRow
|
|
||||||
{
|
|
||||||
private readonly Span<TColor> _pixels;
|
|
||||||
|
|
||||||
public IColor this[int x] => _pixels[x];
|
|
||||||
|
|
||||||
public ScreemImageRow(Span<TColor> pixels)
|
|
||||||
{
|
|
||||||
this._pixels = pixels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -41,10 +41,6 @@
|
|||||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)'=='net6.0'">
|
|
||||||
<EnablePreviewFeatures>true</EnablePreviewFeatures>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
|
||||||
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
|
<DefineConstants>$(DefineConstants);TRACE;DEBUG</DefineConstants>
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user