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