Added Tests for Images; Small improvements

This commit is contained in:
Darth Affe 2023-09-06 21:49:13 +02:00
parent adacf41d07
commit ec57728521
13 changed files with 559 additions and 2 deletions

View File

@ -7,6 +7,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCapture.NET", "Screen
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCapture.NET.DX11", "ScreenCapture.NET.DX11\ScreenCapture.NET.DX11.csproj", "{58A09AD8-D66F-492E-8BC7-62BDB85E57EC}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScreenCapture.NET.DX11", "ScreenCapture.NET.DX11\ScreenCapture.NET.DX11.csproj", "{58A09AD8-D66F-492E-8BC7-62BDB85E57EC}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CF7A1475-3A44-4870-A80F-5988DA25418B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScreenCapture.NET.Tests", "Tests\ScreenCapture.NET.Tests\ScreenCapture.NET.Tests.csproj", "{AA1829BB-EFA7-4BB8-8041-76374659A42B}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -21,10 +25,17 @@ Global
{58A09AD8-D66F-492E-8BC7-62BDB85E57EC}.Debug|Any CPU.Build.0 = Debug|Any CPU {58A09AD8-D66F-492E-8BC7-62BDB85E57EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58A09AD8-D66F-492E-8BC7-62BDB85E57EC}.Release|Any CPU.ActiveCfg = Release|Any CPU {58A09AD8-D66F-492E-8BC7-62BDB85E57EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58A09AD8-D66F-492E-8BC7-62BDB85E57EC}.Release|Any CPU.Build.0 = Release|Any CPU {58A09AD8-D66F-492E-8BC7-62BDB85E57EC}.Release|Any CPU.Build.0 = Release|Any CPU
{AA1829BB-EFA7-4BB8-8041-76374659A42B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA1829BB-EFA7-4BB8-8041-76374659A42B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA1829BB-EFA7-4BB8-8041-76374659A42B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA1829BB-EFA7-4BB8-8041-76374659A42B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{AA1829BB-EFA7-4BB8-8041-76374659A42B} = {CF7A1475-3A44-4870-A80F-5988DA25418B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B5111031-6E65-4331-9E6E-A07165289860} SolutionGuid = {B5111031-6E65-4331-9E6E-A07165289860}
EndGlobalSection EndGlobalSection

View File

@ -1,4 +1,5 @@
<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"> <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">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BGR/@EntryIndexedValue">BGR</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BGRA/@EntryIndexedValue">BGRA</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=BGRA/@EntryIndexedValue">BGRA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DPI/@EntryIndexedValue">DPI</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DPI/@EntryIndexedValue">DPI</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DX/@EntryIndexedValue">DX</s:String></wpf:ResourceDictionary> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DX/@EntryIndexedValue">DX</s:String></wpf:ResourceDictionary>

View File

@ -0,0 +1,47 @@
// ReSharper disable ConvertToAutoProperty
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ScreenCapture.NET;
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct ColorARGB : IColor
{
#region Properties & Fields
public static ColorFormat ColorFormat => ColorFormat.ARGB;
private readonly byte _a;
private readonly byte _r;
private readonly byte _g;
private readonly byte _b;
// ReSharper disable ConvertToAutoPropertyWhenPossible
public byte A => _a;
public byte R => _r;
public byte G => _g;
public byte B => _b;
// ReSharper restore ConvertToAutoPropertyWhenPossible
#endregion
#region Constructors
public ColorARGB(byte a, byte r, byte g, byte b)
{
this._a = a;
this._r = r;
this._g = g;
this._b = b;
}
#endregion
#region Methods
public override string ToString() => $"[A: {_a}, R: {_r}, G: {_g}, B: {_b}]";
#endregion
}

View File

@ -0,0 +1,45 @@
// ReSharper disable ConvertToAutoProperty
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ScreenCapture.NET;
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct ColorBGR : IColor
{
#region Properties & Fields
public static ColorFormat ColorFormat => ColorFormat.BGR;
private readonly byte _b;
private readonly byte _g;
private readonly byte _r;
// ReSharper disable ConvertToAutoPropertyWhenPossible
public byte A => byte.MaxValue;
public byte B => _b;
public byte G => _g;
public byte R => _r;
// ReSharper restore ConvertToAutoPropertyWhenPossible
#endregion
#region Constructors
public ColorBGR(byte b, byte g, byte r)
{
this._b = b;
this._g = g;
this._r = r;
}
#endregion
#region Methods
public override string ToString() => $"[A: {A}, R: {_r}, G: {_g}, B: {_b}]";
#endregion
}

View File

@ -5,6 +5,9 @@ public readonly struct ColorFormat
#region Instances #region Instances
public static readonly ColorFormat BGRA = new(1, 4); public static readonly ColorFormat BGRA = new(1, 4);
public static readonly ColorFormat ARGB = new(2, 4);
public static readonly ColorFormat RGB = new(3, 3);
public static readonly ColorFormat BGR = new(4, 3);
#endregion #endregion

View File

@ -0,0 +1,45 @@
// ReSharper disable ConvertToAutoProperty
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ScreenCapture.NET;
[DebuggerDisplay("[A: {A}, R: {R}, G: {G}, B: {B}]")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct ColorRGB : IColor
{
#region Properties & Fields
public static ColorFormat ColorFormat => ColorFormat.RGB;
private readonly byte _r;
private readonly byte _g;
private readonly byte _b;
// ReSharper disable ConvertToAutoPropertyWhenPossible
public byte A => byte.MaxValue;
public byte R => _r;
public byte G => _g;
public byte B => _b;
// ReSharper restore ConvertToAutoPropertyWhenPossible
#endregion
#region Constructors
public ColorRGB(byte r, byte g, byte b)
{
this._r = r;
this._g = g;
this._b = b;
}
#endregion
#region Methods
public override string ToString() => $"[A: {A}, R: {_r}, G: {_g}, B: {_b}]";
#endregion
}

View File

@ -15,21 +15,25 @@ public interface IImage : IEnumerable<IColor>
public interface IImageRows : IEnumerable<IImageRow> public interface IImageRows : IEnumerable<IImageRow>
{ {
int Count { get; }
IImageRow this[int column] { get; } IImageRow this[int column] { get; }
} }
public interface IImageColumns : IEnumerable<IImageColumn> public interface IImageColumns : IEnumerable<IImageColumn>
{ {
int Count { get; }
IImageColumn this[int column] { get; } IImageColumn this[int column] { get; }
} }
public interface IImageRow : IEnumerable<IColor> public interface IImageRow : IEnumerable<IColor>
{ {
int Length { get; }
IColor this[int x] { get; } IColor this[int x] { get; }
} }
public interface IImageColumn : IEnumerable<IColor> public interface IImageColumn : IEnumerable<IColor>
{ {
int Length { get; }
IColor this[int y] { get; } IColor this[int y] { get; }
} }
} }

View File

@ -40,7 +40,7 @@ public sealed class Image<TColor> : IImage
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
{ {
if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) || ((x + width) >= Width) || ((y + height) >= Height)) throw new IndexOutOfRangeException(); if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) || ((x + width) > Width) || ((y + height) > Height)) throw new IndexOutOfRangeException();
return new Image<TColor>(_buffer, _x + x, _y + y, width, height, _stride); return new Image<TColor>(_buffer, _x + x, _y + y, width, height, _stride);
} }
@ -100,6 +100,8 @@ public sealed class Image<TColor> : IImage
private readonly int _height; private readonly int _height;
private readonly int _stride; private readonly int _stride;
public int Count => _height;
#endregion #endregion
#region Indexer #region Indexer
@ -151,6 +153,8 @@ public sealed class Image<TColor> : IImage
private readonly int _start; private readonly int _start;
private readonly int _length; private readonly int _length;
public int Length => _length;
#endregion #endregion
#region Indexer #region Indexer
@ -203,6 +207,8 @@ public sealed class Image<TColor> : IImage
private readonly int _height; private readonly int _height;
private readonly int _stride; private readonly int _stride;
public int Count => _width;
#endregion #endregion
#region Indexer #region Indexer
@ -255,6 +261,8 @@ public sealed class Image<TColor> : IImage
private readonly int _length; private readonly int _length;
private readonly int _step; private readonly int _step;
public int Length => _length;
#endregion #endregion
#region Indexer #region Indexer

View File

@ -38,7 +38,7 @@ public readonly ref struct RefImage<TColor>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
{ {
if ((x < 0) || (y < 0) || (width <= 0) || (height <= 0) || ((x + width) >= Width) || ((y + height) >= Height)) throw new IndexOutOfRangeException(); 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, _stride);
} }
@ -151,6 +151,8 @@ public readonly ref struct RefImage<TColor>
private readonly int _height; private readonly int _height;
private readonly int _stride; private readonly int _stride;
public int Count => _height;
#endregion #endregion
#region Indexer #region Indexer
@ -243,6 +245,8 @@ public readonly ref struct RefImage<TColor>
private readonly int _height; private readonly int _height;
private readonly int _stride; private readonly int _stride;
public int Count => _width;
#endregion #endregion
#region Indexer #region Indexer

View File

@ -0,0 +1,165 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ScreenCapture.NET.Tests;
[TestClass]
public class ImageTest
{
#region Properties & Fields
private static IScreenCapture? _screenCapture;
private static ICaptureZone? _captureZone;
#endregion
#region Methods
[ClassInitialize]
public static void ClassInit(TestContext _)
{
_screenCapture = new TestScreenCapture();
_captureZone = _screenCapture.RegisterCaptureZone(0, 0, _screenCapture.Display.Width, _screenCapture.Display.Height);
_screenCapture.CaptureScreen();
}
[ClassCleanup]
public static void ClassCleanup()
{
_screenCapture?.Dispose();
_screenCapture = null;
}
[TestMethod]
public void TestImageFullScreen()
{
IImage image = _captureZone!.Image;
Assert.AreEqual(_captureZone.Width, image.Width);
Assert.AreEqual(_captureZone.Height, image.Height);
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), image[x, y]);
}
[TestMethod]
public void TestImageInnerFull()
{
IImage image = _captureZone!.Image;
image = image[0, 0, image.Width, image.Height];
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), image[x, y]);
}
[TestMethod]
public void TestImageEnumerator()
{
IImage image = _captureZone!.Image;
int counter = 0;
foreach (IColor color in image)
{
int x = counter % image.Width;
int y = counter / image.Width;
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), color);
counter++;
}
}
[TestMethod]
public void TestImageInnerPartial()
{
IImage image = _captureZone!.Image;
image = image[163, 280, 720, 13];
Assert.AreEqual(720, image.Width);
Assert.AreEqual(13, image.Height);
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(163 + x, 280 + y), image[x, y]);
}
[TestMethod]
public void TestImageInnerInnerPartial()
{
IImage image = _captureZone!.Image;
image = image[163, 280, 720, 13];
image = image[15, 2, 47, 8];
Assert.AreEqual(47, image.Width);
Assert.AreEqual(8, image.Height);
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(178 + x, 282 + y), image[x, y]);
}
[TestMethod]
public void TestImageRowIndexer()
{
IImage image = _captureZone!.Image;
Assert.AreEqual(image.Height, image.Rows.Count);
for (int y = 0; y < image.Height; y++)
{
IImage.IImageRow row = image.Rows[y];
Assert.AreEqual(image.Width, row.Length);
for (int x = 0; x < row.Length; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), row[x]);
}
}
[TestMethod]
public void TestImageRowEnumerator()
{
IImage image = _captureZone!.Image;
int y = 0;
foreach (IImage.IImageRow row in image.Rows)
{
for (int x = 0; x < row.Length; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), row[x]);
y++;
}
}
[TestMethod]
public void TestImageColumnIndexer()
{
IImage image = _captureZone!.Image;
Assert.AreEqual(image.Width, image.Columns.Count);
for (int x = 0; x < image.Width; x++)
{
IImage.IImageColumn column = image.Columns[x];
Assert.AreEqual(image.Height, column.Length);
for (int y = 0; y < column.Length; y++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), column[y]);
}
}
[TestMethod]
public void TestImageColumnEnumerator()
{
IImage image = _captureZone!.Image;
int x = 0;
foreach (IImage.IImageColumn column in image.Columns)
{
for (int y = 0; y < column.Length; y++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), column[y]);
x++;
}
}
#endregion
}

View File

@ -0,0 +1,165 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ScreenCapture.NET.Tests;
[TestClass]
public class RefImageTest
{
#region Properties & Fields
private static TestScreenCapture? _screenCapture;
private static CaptureZone<ColorARGB>? _captureZone;
#endregion
#region Methods
[ClassInitialize]
public static void ClassInit(TestContext _)
{
_screenCapture = new TestScreenCapture();
_captureZone = _screenCapture.RegisterCaptureZone(0, 0, _screenCapture.Display.Width, _screenCapture.Display.Height);
_screenCapture.CaptureScreen();
}
[ClassCleanup]
public static void ClassCleanup()
{
_screenCapture?.Dispose();
_screenCapture = null;
}
[TestMethod]
public void TestImageFullScreen()
{
RefImage<ColorARGB> image = _captureZone!.Image;
Assert.AreEqual(_captureZone.Width, image.Width);
Assert.AreEqual(_captureZone.Height, image.Height);
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), image[x, y]);
}
[TestMethod]
public void TestImageInnerFull()
{
RefImage<ColorARGB> image = _captureZone!.Image;
image = image[0, 0, image.Width, image.Height];
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), image[x, y]);
}
[TestMethod]
public void TestImageEnumerator()
{
RefImage<ColorARGB> image = _captureZone!.Image;
int counter = 0;
foreach (ColorARGB color in image)
{
int x = counter % image.Width;
int y = counter / image.Width;
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), color);
counter++;
}
}
[TestMethod]
public void TestImageInnerPartial()
{
RefImage<ColorARGB> image = _captureZone!.Image;
image = image[163, 280, 720, 13];
Assert.AreEqual(720, image.Width);
Assert.AreEqual(13, image.Height);
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(163 + x, 280 + y), image[x, y]);
}
[TestMethod]
public void TestImageInnerInnerPartial()
{
RefImage<ColorARGB> image = _captureZone!.Image;
image = image[163, 280, 720, 13];
image = image[15, 2, 47, 8];
Assert.AreEqual(47, image.Width);
Assert.AreEqual(8, image.Height);
for (int y = 0; y < image.Height; y++)
for (int x = 0; x < image.Width; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(178 + x, 282 + y), image[x, y]);
}
[TestMethod]
public void TestImageRowIndexer()
{
RefImage<ColorARGB> image = _captureZone!.Image;
Assert.AreEqual(image.Height, image.Rows.Count);
for (int y = 0; y < image.Height; y++)
{
ReadOnlyRefEnumerable<ColorARGB> row = image.Rows[y];
Assert.AreEqual(image.Width, row.Length);
for (int x = 0; x < row.Length; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), row[x]);
}
}
[TestMethod]
public void TestImageRowEnumerator()
{
RefImage<ColorARGB> image = _captureZone!.Image;
int y = 0;
foreach (ReadOnlyRefEnumerable<ColorARGB> row in image.Rows)
{
for (int x = 0; x < row.Length; x++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), row[x]);
y++;
}
}
[TestMethod]
public void TestImageColumnIndexer()
{
RefImage<ColorARGB> image = _captureZone!.Image;
Assert.AreEqual(image.Width, image.Columns.Count);
for (int x = 0; x < image.Width; x++)
{
ReadOnlyRefEnumerable<ColorARGB> column = image.Columns[x];
Assert.AreEqual(image.Height, column.Length);
for (int y = 0; y < column.Length; y++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), column[y]);
}
}
[TestMethod]
public void TestImageColumnEnumerator()
{
RefImage<ColorARGB> image = _captureZone!.Image;
int x = 0;
foreach (ReadOnlyRefEnumerable<ColorARGB> column in image.Columns)
{
for (int y = 0; y < column.Length; y++)
Assert.AreEqual(TestScreenCapture.GetColorFromLocation(x, y), column[y]);
x++;
}
}
#endregion
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ScreenCapture.NET\ScreenCapture.NET.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,37 @@
using System;
using System.Runtime.InteropServices;
namespace ScreenCapture.NET.Tests;
internal class TestScreenCapture : AbstractScreenCapture<ColorARGB>
{
#region Constructors
public TestScreenCapture(Rotation rotation = Rotation.None)
: base(new Display(0, "Test", 1920, 1080, rotation, new GraphicsCard(0, "Test", 0, 0)))
{ }
#endregion
#region Methods
protected override bool PerformScreenCapture() => true;
protected override void PerformCaptureZoneUpdate(CaptureZone<ColorARGB> captureZone, in Span<byte> buffer)
{
Span<ColorARGB> pixels = MemoryMarshal.Cast<byte, ColorARGB>(buffer);
for (int y = 0; y < captureZone.Height; y++)
for (int x = 0; x < captureZone.Width; x++)
pixels[(y * captureZone.Width) + x] = GetColorFromLocation(x, y);
}
public static ColorARGB GetColorFromLocation(int x, int y)
{
byte[] xBytes = BitConverter.GetBytes((short)x);
byte[] yBytes = BitConverter.GetBytes((short)y);
return new ColorARGB(xBytes[0], xBytes[1], yBytes[0], yBytes[1]);
}
#endregion
}