mirror of https://github.com/SixLabors/ImageSharp
8 changed files with 260 additions and 27 deletions
@ -0,0 +1,102 @@ |
|||
// <copyright file="Fast2DArray{T}.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
/// <summary>
|
|||
/// Provides fast access to 2D arrays.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The type of elements in the array.</typeparam>
|
|||
public struct Fast2DArray<T> |
|||
{ |
|||
/// <summary>
|
|||
/// The 1D representation of the 2D array.
|
|||
/// </summary>
|
|||
public T[] Data; |
|||
|
|||
/// <summary>
|
|||
/// Gets the width of the 2D array.
|
|||
/// </summary>
|
|||
public int Width; |
|||
|
|||
/// <summary>
|
|||
/// Gets the height of the 2D array.
|
|||
/// </summary>
|
|||
public int Height; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="Fast2DArray{T}"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="data">The 2D array to provide access to.</param>
|
|||
public Fast2DArray(T[,] data) |
|||
{ |
|||
Guard.NotNull(data, nameof(data)); |
|||
this.Height = data.GetLength(0); |
|||
this.Width = data.GetLength(1); |
|||
|
|||
Guard.MustBeGreaterThan(this.Width, 0, nameof(this.Width)); |
|||
Guard.MustBeGreaterThan(this.Height, 0, nameof(this.Height)); |
|||
|
|||
this.Data = new T[this.Width * this.Height]; |
|||
|
|||
for (int y = 0; y < this.Height; y++) |
|||
{ |
|||
for (int x = 0; x < this.Width; x++) |
|||
{ |
|||
this.Data[(y * this.Width) + x] = data[y, x]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the item at the specified position.
|
|||
/// </summary>
|
|||
/// <param name="row">The row-coordinate of the item. Must be greater than or equal to zero and less than the height of the array.</param>
|
|||
/// <param name="column">The column-coordinate of the item. Must be greater than or equal to zero and less than the width of the array.</param>
|
|||
/// <returns>The <see typeparam="T"/> at the specified position.</returns>
|
|||
public T this[int row, int column] |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
this.CheckCoordinates(row, column); |
|||
return this.Data[(row * this.Width) + column]; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set |
|||
{ |
|||
this.CheckCoordinates(row, column); |
|||
this.Data[(row * this.Width) + column] = value; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Checks the coordinates to ensure they are within bounds.
|
|||
/// </summary>
|
|||
/// <param name="row">The row-coordinate of the item. Must be greater than zero and smaller than the height of the array.</param>
|
|||
/// <param name="column">The column-coordinate of the item. Must be greater than zero and smaller than the width of the array.</param>
|
|||
/// <exception cref="ArgumentOutOfRangeException">
|
|||
/// Thrown if the coordinates are not within the bounds of the array.
|
|||
/// </exception>
|
|||
[Conditional("DEBUG")] |
|||
private void CheckCoordinates(int row, int column) |
|||
{ |
|||
if (row < 0 || row >= this.Height) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(row), row, $"{row} is outwith the array bounds."); |
|||
} |
|||
|
|||
if (column < 0 || column >= this.Width) |
|||
{ |
|||
throw new ArgumentOutOfRangeException(nameof(column), column, $"{column} is outwith the array bounds."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
// <copyright file="Array2D.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Benchmarks.General |
|||
{ |
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
public class Array2D |
|||
{ |
|||
private float[,] data; |
|||
|
|||
private float[][] jaggedData; |
|||
|
|||
private Fast2DArray<float> fastData; |
|||
|
|||
[Params(10, 100, 1000, 10000)] |
|||
public int Count { get; set; } |
|||
|
|||
public int Index { get; set; } |
|||
|
|||
[Setup] |
|||
public void SetUp() |
|||
{ |
|||
this.data = new float[this.Count, this.Count]; |
|||
this.jaggedData = new float[this.Count][]; |
|||
|
|||
for (int i = 0; i < this.Count; i++) |
|||
{ |
|||
this.jaggedData[i] = new float[this.Count]; |
|||
} |
|||
|
|||
this.fastData = new Fast2DArray<float>(this.data); |
|||
|
|||
this.Index = this.Count / 2; |
|||
} |
|||
|
|||
[Benchmark(Baseline = true, Description = "Array access using 2D array")] |
|||
public float ArrayIndex() |
|||
{ |
|||
return this.data[this.Index, this.Index]; |
|||
} |
|||
|
|||
[Benchmark(Description = "Array access using a jagged array")] |
|||
public float ArrayJaggedIndex() |
|||
{ |
|||
return this.jaggedData[this.Index][this.Index]; |
|||
} |
|||
|
|||
[Benchmark(Description = "Array access using Fast2DArray")] |
|||
public float ArrayFastIndex() |
|||
{ |
|||
return this.fastData[this.Index, this.Index]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// <copyright file="Fast2DArrayTests.cs" company="James Jackson-South">
|
|||
// Copyright (c) James Jackson-South and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
// </copyright>
|
|||
|
|||
namespace ImageSharp.Tests.Common |
|||
{ |
|||
using System; |
|||
|
|||
using Xunit; |
|||
|
|||
public class Fast2DArrayTests |
|||
{ |
|||
private static readonly float[,] FloydSteinbergMatrix = |
|||
{ |
|||
{ 0, 0, 7 }, |
|||
{ 3, 5, 1 } |
|||
}; |
|||
|
|||
[Fact] |
|||
public void Fast2DArrayThrowsOnNullInitializer() |
|||
{ |
|||
Assert.Throws<ArgumentNullException>(() => |
|||
{ |
|||
Fast2DArray<float> fast = new Fast2DArray<float>(null); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Fast2DArrayThrowsOnEmptyInitializer() |
|||
{ |
|||
Assert.Throws<ArgumentOutOfRangeException>(() => |
|||
{ |
|||
Fast2DArray<float> fast = new Fast2DArray<float>(new float[0, 0]); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Fast2DArrayReturnsCorrectDimensions() |
|||
{ |
|||
Fast2DArray<float> fast = new Fast2DArray<float>(FloydSteinbergMatrix); |
|||
Assert.True(fast.Width == FloydSteinbergMatrix.GetLength(1)); |
|||
Assert.True(fast.Height == FloydSteinbergMatrix.GetLength(0)); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Fast2DArrayGetReturnsCorrectResults() |
|||
{ |
|||
Fast2DArray<float> fast = new Fast2DArray<float>(FloydSteinbergMatrix); |
|||
|
|||
for (int row = 0; row < fast.Height; row++) |
|||
{ |
|||
for (int column = 0; column < fast.Width; column++) |
|||
{ |
|||
Assert.True(Math.Abs(fast[row, column] - FloydSteinbergMatrix[row, column]) < Constants.Epsilon); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Fast2DArrayGetSetReturnsCorrectResults() |
|||
{ |
|||
Fast2DArray<float> fast = new Fast2DArray<float>(new float[4, 4]); |
|||
const float Val = 5F; |
|||
|
|||
fast[3, 3] = Val; |
|||
|
|||
Assert.True(Math.Abs(Val - fast[3, 3]) < Constants.Epsilon); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue