Browse Source

buffer.CopyColumns(...)

pull/888/head
Anton Firszov 7 years ago
parent
commit
19766df11a
  1. 144
      src/ImageSharp/Memory/Buffer2DExtensions.cs
  2. 28
      tests/ImageSharp.Tests/Memory/Buffer2DTests.cs
  3. 13
      tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs

144
src/ImageSharp/Memory/Buffer2DExtensions.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using SixLabors.Primitives;
@ -14,41 +15,90 @@ namespace SixLabors.ImageSharp.Memory
internal static class Buffer2DExtensions
{
/// <summary>
/// Gets a <see cref="Span{T}"/> to the backing buffer of <paramref name="buffer"/>.
/// Copy <paramref name="columnCount"/> columns of <paramref name="buffer"/> inplace,
/// from positions starting at <paramref name="sourceIndex"/> to positions at <paramref name="destIndex"/>.
/// </summary>
internal static Span<T> GetSpan<T>(this Buffer2D<T> buffer)
public static unsafe void CopyColumns<T>(
this Buffer2D<T> buffer,
int sourceIndex,
int destIndex,
int columnCount)
where T : struct
{
return buffer.MemorySource.GetSpan();
DebugGuard.NotNull(buffer, nameof(buffer));
DebugGuard.MustBeGreaterThanOrEqualTo(sourceIndex, 0, nameof(sourceIndex));
DebugGuard.MustBeGreaterThanOrEqualTo(destIndex, sourceIndex + columnCount, nameof(destIndex));
DebugGuard.MustBeLessThanOrEqualTo(destIndex, buffer.Width - columnCount, nameof(destIndex));
int elementSize = Unsafe.SizeOf<T>();
int width = buffer.Width * elementSize;
int sOffset = sourceIndex * elementSize;
int dOffset = destIndex * elementSize;
long count = columnCount * elementSize;
using (MemoryHandle handle = buffer.Memory.Pin())
{
byte* basePtr = (byte*)handle.Pointer;
for (int y = 0; y < buffer.Height; y++)
{
byte* sPtr = basePtr + sOffset;
byte* dPtr = basePtr + dOffset;
Buffer.MemoryCopy(sPtr, dPtr, count, count);
basePtr += width;
}
}
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// Returns a <see cref="Rectangle"/> representing the full area of the buffer.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="x">The x coordinate (position in the row)</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int x, int y)
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Rectangle"/></returns>
public static Rectangle FullRectangle<T>(this Buffer2D<T> buffer)
where T : struct
{
return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x);
return new Rectangle(0, 0, buffer.Width, buffer.Height);
}
/// <summary>
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// Return a <see cref="BufferArea{T}"/> to the subarea represented by 'rectangle'
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int y)
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <param name="rectangle">The rectangle subarea</param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, in Rectangle rectangle)
where T : struct =>
new BufferArea<T>(buffer, rectangle);
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, int x, int y, int width, int height)
where T : struct =>
new BufferArea<T>(buffer, new Rectangle(x, y, width, height));
/// <summary>
/// Return a <see cref="BufferArea{T}"/> to the whole area of 'buffer'
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer)
where T : struct =>
new BufferArea<T>(buffer);
public static BufferArea<T> GetAreaBetweenRows<T>(this Buffer2D<T> buffer, int minY, int maxY)
where T : struct =>
new BufferArea<T>(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY));
/// <summary>
/// Gets a span for all the pixels in <paramref name="buffer"/> defined by <paramref name="rows"/>
/// </summary>
public static Span<T> GetMultiRowSpan<T>(this Buffer2D<T> buffer, in RowInterval rows)
where T : struct
{
return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width);
return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width);
}
/// <summary>
@ -66,61 +116,53 @@ namespace SixLabors.ImageSharp.Memory
}
/// <summary>
/// Returns the size of the buffer.
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="x">The x coordinate (position in the row)</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Size{T}"/> of the buffer</returns>
public static Size Size<T>(this Buffer2D<T> buffer)
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int x, int y)
where T : struct
{
return new Size(buffer.Width, buffer.Height);
return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x);
}
/// <summary>
/// Returns a <see cref="Rectangle"/> representing the full area of the buffer.
/// Gets a <see cref="Span{T}"/> to the row 'y' beginning from the pixel at the first pixel on that row.
/// </summary>
/// <param name="buffer">The buffer</param>
/// <param name="y">The y (row) coordinate</param>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="Rectangle"/></returns>
public static Rectangle FullRectangle<T>(this Buffer2D<T> buffer)
/// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span<T> GetRowSpan<T>(this Buffer2D<T> buffer, int y)
where T : struct
{
return new Rectangle(0, 0, buffer.Width, buffer.Height);
return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width);
}
/// <summary>
/// Return a <see cref="BufferArea{T}"/> to the subarea represented by 'rectangle'
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <param name="rectangle">The rectangle subarea</param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, in Rectangle rectangle)
where T : struct => new BufferArea<T>(buffer, rectangle);
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer, int x, int y, int width, int height)
where T : struct => new BufferArea<T>(buffer, new Rectangle(x, y, width, height));
public static BufferArea<T> GetAreaBetweenRows<T>(this Buffer2D<T> buffer, int minY, int maxY)
where T : struct => new BufferArea<T>(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY));
/// <summary>
/// Return a <see cref="BufferArea{T}"/> to the whole area of 'buffer'
/// Returns the size of the buffer.
/// </summary>
/// <typeparam name="T">The element type</typeparam>
/// <param name="buffer">The <see cref="Buffer2D{T}"/></param>
/// <returns>The <see cref="BufferArea{T}"/></returns>
public static BufferArea<T> GetArea<T>(this Buffer2D<T> buffer)
where T : struct => new BufferArea<T>(buffer);
/// <returns>The <see cref="Size{T}"/> of the buffer</returns>
public static Size Size<T>(this Buffer2D<T> buffer)
where T : struct
{
return new Size(buffer.Width, buffer.Height);
}
/// <summary>
/// Gets a span for all the pixels in <paramref name="buffer"/> defined by <paramref name="rows"/>
/// Gets a <see cref="Span{T}"/> to the backing buffer of <paramref name="buffer"/>.
/// </summary>
public static Span<T> GetMultiRowSpan<T>(this Buffer2D<T> buffer, in RowInterval rows)
internal static Span<T> GetSpan<T>(this Buffer2D<T> buffer)
where T : struct
{
return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width);
return buffer.MemorySource.GetSpan();
}
}
}

28
tests/ImageSharp.Tests/Memory/Buffer2DTests.cs

@ -127,5 +127,33 @@ namespace SixLabors.ImageSharp.Tests.Memory
Assert.Equal(new Size(10, 5), b.Size());
}
}
[Theory]
[InlineData(100, 20, 0, 90, 10)]
[InlineData(100, 3, 0, 50, 50)]
[InlineData(123, 23, 10, 80, 13)]
[InlineData(10, 1, 3, 6, 3)]
[InlineData(2, 2, 0, 1, 1)]
[InlineData(5, 1, 1, 3, 2)]
public void CopyColumns(int width, int height, int startIndex, int destIndex, int columnCount)
{
Random rnd = new Random(123);
using (Buffer2D<float> b = this.MemoryAllocator.Allocate2D<float>(width, height))
{
rnd.RandomFill(b.Span, 0, 1);
b.CopyColumns(startIndex, destIndex, columnCount);
for (int y = 0; y < b.Height; y++)
{
Span<float> row = b.GetRowSpan(y);
Span<float> s = row.Slice(startIndex, columnCount);
Span<float> d = row.Slice(destIndex, columnCount);
Xunit.Assert.True(s.SequenceEqual(d));
}
}
}
}
}

13
tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs

@ -23,14 +23,19 @@ namespace SixLabors.ImageSharp.Tests
{
float[] values = new float[length];
for (int i = 0; i < length; i++)
{
values[i] = GetRandomFloat(rnd, minVal, maxVal);
}
RandomFill(rnd, values, minVal, maxVal);
return values;
}
public static void RandomFill(this Random rnd, Span<float> destination, float minVal, float maxVal)
{
for (int i = 0; i < destination.Length; i++)
{
destination[i] = GetRandomFloat(rnd, minVal, maxVal);
}
}
/// <summary>
/// Creates an <see cref="Vector4[]"/> of the given length consisting of random values between the two ranges.
/// </summary>

Loading…
Cancel
Save