mirror of
https://github.com/DarthAffe/ScreenCapture.NET.git
synced 2025-12-12 13:28:35 +00:00
Added ToArray and CopyTo Methods to Images; Made RefImage pinnable
This commit is contained in:
parent
f04d270d8e
commit
b8b6423005
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ScreenCapture.NET;
|
||||
|
||||
@ -7,6 +8,11 @@ namespace ScreenCapture.NET;
|
||||
/// </summary>
|
||||
public interface IImage : IEnumerable<IColor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the color format used in this image.
|
||||
/// </summary>
|
||||
ColorFormat ColorFormat { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of this image.
|
||||
/// </summary>
|
||||
@ -17,6 +23,11 @@ public interface IImage : IEnumerable<IColor>
|
||||
/// </summary>
|
||||
int Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size in bytes of this image.
|
||||
/// </summary>
|
||||
int SizeInBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the color at the specified location.
|
||||
/// </summary>
|
||||
@ -45,6 +56,28 @@ public interface IImage : IEnumerable<IColor>
|
||||
/// </summary>
|
||||
IImageColumns Columns { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets an <see cref="RefImage{TColor}"/> representing this <see cref="IImage"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="TColor">The color-type of the iamge.</typeparam>
|
||||
/// <returns>The <inheritdoc cref="RefImage{TColor}"/>.</returns>
|
||||
RefImage<TColor> AsRefImage<TColor>() where TColor : struct, IColor;
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="IImage"/> 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="IImage"/> instance.
|
||||
/// </exception>
|
||||
void CopyTo(in Span<byte> destination);
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new array and copies this <see cref="IImage"/> into it.
|
||||
/// </summary>
|
||||
/// <returns>The new array containing the data of this <see cref="IImage"/>.</returns>
|
||||
byte[] ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// Represents a list of rows of an image.
|
||||
/// </summary>
|
||||
@ -91,12 +124,32 @@ public interface IImage : IEnumerable<IColor>
|
||||
/// </summary>
|
||||
int Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size in bytes of this row.
|
||||
/// </summary>
|
||||
int SizeInBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IColor"/> at the specified location.
|
||||
/// </summary>
|
||||
/// <param name="x">The location to get the color from.</param>
|
||||
/// <returns>The <see cref="IColor"/> at the specified location.</returns>
|
||||
IColor this[int x] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="IImageRow"/> 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="IImageRow"/> instance.
|
||||
/// </exception>
|
||||
void CopyTo(in Span<byte> destination);
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new array and copies this <see cref="IImageRow"/> into it.
|
||||
/// </summary>
|
||||
/// <returns>The new array containing the data of this <see cref="IImageRow"/>.</returns>
|
||||
byte[] ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -109,11 +162,31 @@ public interface IImage : IEnumerable<IColor>
|
||||
/// </summary>
|
||||
int Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size in bytes of this column.
|
||||
/// </summary>
|
||||
int SizeInBytes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IColor"/> at the specified location.
|
||||
/// </summary>
|
||||
/// <param name="y">The location to get the color from.</param>
|
||||
/// <returns>The <see cref="IColor"/> at the specified location.</returns>
|
||||
IColor this[int y] { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="IImageColumn"/> 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="IImageColumn"/> instance.
|
||||
/// </exception>
|
||||
void CopyTo(in Span<byte> destination);
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new array and copies this <see cref="IImageColumn"/> into it.
|
||||
/// </summary>
|
||||
/// <returns>The new array containing the data of this <see cref="IImageColumn"/>.</returns>
|
||||
byte[] ToArray();
|
||||
}
|
||||
}
|
||||
@ -18,12 +18,18 @@ public sealed class Image<TColor> : IImage
|
||||
private readonly int _y;
|
||||
private readonly int _stride;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ColorFormat ColorFormat => TColor.ColorFormat;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Width { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Height { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SizeInBytes => Width * Height * TColor.ColorFormat.BytesPerPixel;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
@ -84,6 +90,39 @@ public sealed class Image<TColor> : IImage
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(in Span<byte> destination)
|
||||
{
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
if (destination.Length < SizeInBytes) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
|
||||
|
||||
int targetStride = Width * TColor.ColorFormat.BytesPerPixel;
|
||||
IImage.IImageRows rows = Rows;
|
||||
Span<byte> target = destination;
|
||||
foreach (IImage.IImageRow row in rows)
|
||||
{
|
||||
row.CopyTo(target);
|
||||
target = target[targetStride..];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ToArray()
|
||||
{
|
||||
byte[] array = new byte[SizeInBytes];
|
||||
CopyTo(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RefImage<T> AsRefImage<T>()
|
||||
where T : struct, IColor
|
||||
{
|
||||
if (typeof(T) != typeof(TColor)) throw new ArgumentException("The requested color format does not fit this image.", nameof(T));
|
||||
|
||||
return new RefImage<T>(MemoryMarshal.Cast<byte, T>(_buffer), _x, _y, Width, Height, _stride);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IColor> GetEnumerator()
|
||||
{
|
||||
@ -172,6 +211,9 @@ public sealed class Image<TColor> : IImage
|
||||
/// <inheritdoc />
|
||||
public int Length => _length;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SizeInBytes => Length * TColor.ColorFormat.BytesPerPixel;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
@ -203,6 +245,23 @@ public sealed class Image<TColor> : IImage
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(in Span<byte> destination)
|
||||
{
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
if (destination.Length < SizeInBytes) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
|
||||
|
||||
_buffer.AsSpan(_start, SizeInBytes).CopyTo(destination);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ToArray()
|
||||
{
|
||||
byte[] array = new byte[SizeInBytes];
|
||||
CopyTo(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IColor> GetEnumerator()
|
||||
{
|
||||
@ -290,6 +349,9 @@ public sealed class Image<TColor> : IImage
|
||||
/// <inheritdoc />
|
||||
public int Length => _length;
|
||||
|
||||
/// <inheritdoc />
|
||||
public int SizeInBytes => Length * TColor.ColorFormat.BytesPerPixel;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
@ -301,8 +363,8 @@ public sealed class Image<TColor> : IImage
|
||||
{
|
||||
if ((y < 0) || (y >= _length)) throw new IndexOutOfRangeException();
|
||||
|
||||
ReadOnlySpan<TColor> row = MemoryMarshal.Cast<byte, TColor>(_buffer)[_start..];
|
||||
return row[y * _step];
|
||||
ReadOnlySpan<TColor> data = MemoryMarshal.Cast<byte, TColor>(_buffer)[_start..];
|
||||
return data[y * _step];
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,6 +384,31 @@ public sealed class Image<TColor> : IImage
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <inheritdoc />
|
||||
public void CopyTo(in Span<byte> destination)
|
||||
{
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
if (destination.Length < SizeInBytes) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
|
||||
|
||||
if (_step == 1)
|
||||
_buffer.AsSpan(_start, SizeInBytes).CopyTo(destination);
|
||||
else
|
||||
{
|
||||
ReadOnlySpan<TColor> data = MemoryMarshal.Cast<byte, TColor>(_buffer)[_start..];
|
||||
Span<TColor> target = MemoryMarshal.Cast<byte, TColor>(destination);
|
||||
for (int i = 0; i < Length; i++)
|
||||
target[i] = data[i * _step];
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[] ToArray()
|
||||
{
|
||||
byte[] array = new byte[SizeInBytes];
|
||||
CopyTo(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerator<IColor> GetEnumerator()
|
||||
{
|
||||
|
||||
@ -13,23 +13,37 @@ public readonly ref struct RefImage<TColor>
|
||||
|
||||
private readonly int _x;
|
||||
private readonly int _y;
|
||||
private readonly int _stride;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of the image.
|
||||
/// </summary>
|
||||
public int Width { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the height of the image.
|
||||
/// </summary>
|
||||
public int Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stride (entries per row) of the underlying buffer.
|
||||
/// Only useful if you want to work with a pinned buffer.
|
||||
/// </summary>
|
||||
public int RawStride { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Indexer
|
||||
|
||||
public TColor this[int x, int y]
|
||||
public ref readonly 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)];
|
||||
ref TColor r0 = ref MemoryMarshal.GetReference(_pixels);
|
||||
nint offset = (nint)(uint)((_y + y) * RawStride) + (_x + x);
|
||||
return ref Unsafe.Add(ref r0, offset);
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,19 +54,19 @@ public readonly ref struct RefImage<TColor>
|
||||
{
|
||||
if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) || ((x + width) > Width) || ((y + height) > Height)) throw new IndexOutOfRangeException();
|
||||
|
||||
return new RefImage<TColor>(_pixels, _x + x, _y + y, width, height, _stride);
|
||||
return new RefImage<TColor>(_pixels, _x + x, _y + y, width, height, RawStride);
|
||||
}
|
||||
}
|
||||
|
||||
public ImageRows Rows
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(_pixels, _x, _y, Width, Height, _stride);
|
||||
get => new(_pixels, _x, _y, Width, Height, RawStride);
|
||||
}
|
||||
public ImageColumns Columns
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(_pixels, _x, _y, Width, Height, _stride);
|
||||
get => new(_pixels, _x, _y, Width, Height, RawStride);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -66,20 +80,27 @@ public readonly ref struct RefImage<TColor>
|
||||
this._y = y;
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
this._stride = stride;
|
||||
this.RawStride = stride;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
public void CopyTo(in Span<TColor> dest)
|
||||
/// <summary>
|
||||
/// Copies the contents of this <see cref="RefImage{TColor}"/> 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="RefImage{TColor}"/> instance.
|
||||
/// </exception>
|
||||
public void CopyTo(in Span<TColor> destination)
|
||||
{
|
||||
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));
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
if (destination.Length < (Width * Height)) throw new ArgumentException("The destination is too small to fit this image.", nameof(destination));
|
||||
|
||||
ImageRows rows = Rows;
|
||||
Span<TColor> target = dest;
|
||||
Span<TColor> target = destination;
|
||||
foreach (ReadOnlyRefEnumerable<TColor> row in rows)
|
||||
{
|
||||
row.CopyTo(target);
|
||||
@ -87,6 +108,10 @@ public readonly ref struct RefImage<TColor>
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates a new array and copies this <see cref="RefImage{TColor}"/> into it.
|
||||
/// </summary>
|
||||
/// <returns>The new array containing the data of this <see cref="RefImage{TColor}"/>.</returns>
|
||||
public TColor[] ToArray()
|
||||
{
|
||||
TColor[] array = new TColor[Width * Height];
|
||||
@ -94,6 +119,18 @@ public readonly ref struct RefImage<TColor>
|
||||
return array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the first element of this image inside the full image buffer.
|
||||
/// </summary>
|
||||
public ref readonly TColor GetPinnableReference()
|
||||
{
|
||||
if (_pixels.Length == 0)
|
||||
return ref Unsafe.NullRef<TColor>();
|
||||
|
||||
int offset = (_y * RawStride) + _x;
|
||||
return ref MemoryMarshal.GetReference(_pixels[offset..]);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="System.Collections.IEnumerable.GetEnumerator"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ImageEnumerator GetEnumerator() => new(_pixels);
|
||||
|
||||
@ -161,5 +161,19 @@ public class ImageTest
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestAsRefImage()
|
||||
{
|
||||
IImage image = _captureZone!.Image;
|
||||
image = image[163, 280, 720, 13];
|
||||
image = image[15, 2, 47, 8];
|
||||
|
||||
RefImage<ColorARGB> refImage = image.AsRefImage<ColorARGB>();
|
||||
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
Assert.AreEqual(image[x, y], refImage[x, y]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user