Browse Source

goodby MutableSpan!

pull/322/head
Anton Firszov 9 years ago
parent
commit
492c32ef45
  1. 39
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  2. 6
      src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegPixelArea.cs
  3. 98
      src/ImageSharp/Formats/Jpeg/GolangPort/Utils/MutableSpan.cs
  4. 61
      src/ImageSharp/Formats/Jpeg/GolangPort/Utils/MutableSpanExtensions.cs
  5. 46
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  6. 14
      tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs
  7. 66
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs
  8. 28
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs

39
src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs

@ -5,11 +5,12 @@ using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Jpeg.Common namespace SixLabors.ImageSharp.Formats.Jpeg.Common
{ {
using SixLabors.ImageSharp.Memory;
/// <summary> /// <summary>
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd /// DCT code Ported from https://github.com/norishigefukushima/dct_simd
/// </summary> /// </summary>
@ -86,7 +87,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
} }
} }
} }
/// <summary> /// <summary>
/// Pointer-based "Indexer" (getter part) /// Pointer-based "Indexer" (getter part)
/// </summary> /// </summary>
@ -128,12 +129,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// </summary> /// </summary>
/// <param name="source">Source</param> /// <param name="source">Source</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void LoadFrom(MutableSpan<float> source) public void LoadFrom(Span<float> source)
{ {
fixed (void* ptr = &this.V0L) ref byte s = ref Unsafe.As<float, byte>(ref source.DangerousGetPinnableReference());
{ ref byte d = ref Unsafe.As<Block8x8F, byte>(ref this);
Marshal.Copy(source.Data, source.Offset, (IntPtr)ptr, ScalarCount);
} Unsafe.CopyBlock(ref d, ref s, ScalarCount * sizeof(float));
} }
/// <summary> /// <summary>
@ -142,16 +143,16 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <param name="blockPtr">Block pointer</param> /// <param name="blockPtr">Block pointer</param>
/// <param name="source">Source</param> /// <param name="source">Source</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void LoadFrom(Block8x8F* blockPtr, MutableSpan<float> source) public static unsafe void LoadFrom(Block8x8F* blockPtr, Span<float> source)
{ {
Marshal.Copy(source.Data, source.Offset, (IntPtr)blockPtr, ScalarCount); blockPtr->LoadFrom(source);
} }
/// <summary> /// <summary>
/// Load raw 32bit floating point data from source /// Load raw 32bit floating point data from source
/// </summary> /// </summary>
/// <param name="source">Source</param> /// <param name="source">Source</param>
public unsafe void LoadFrom(MutableSpan<int> source) public unsafe void LoadFrom(Span<int> source)
{ {
fixed (Vector4* ptr = &this.V0L) fixed (Vector4* ptr = &this.V0L)
{ {
@ -168,12 +169,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// </summary> /// </summary>
/// <param name="dest">Destination</param> /// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void CopyTo(MutableSpan<float> dest) public unsafe void CopyTo(Span<float> dest)
{ {
fixed (void* ptr = &this.V0L) ref byte d = ref Unsafe.As<float, byte>(ref dest.DangerousGetPinnableReference());
{ ref byte s = ref Unsafe.As<Block8x8F, byte>(ref this);
Marshal.Copy((IntPtr)ptr, dest.Data, dest.Offset, ScalarCount);
} Unsafe.CopyBlock(ref d, ref s, ScalarCount * sizeof(float));
} }
/// <summary> /// <summary>
@ -182,7 +183,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <param name="blockPtr">Pointer to block</param> /// <param name="blockPtr">Pointer to block</param>
/// <param name="dest">Destination</param> /// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan<byte> dest) public static unsafe void CopyTo(Block8x8F* blockPtr, Span<byte> dest)
{ {
float* fPtr = (float*)blockPtr; float* fPtr = (float*)blockPtr;
for (int i = 0; i < ScalarCount; i++) for (int i = 0; i < ScalarCount; i++)
@ -198,9 +199,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// <param name="blockPtr">Block pointer</param> /// <param name="blockPtr">Block pointer</param>
/// <param name="dest">Destination</param> /// <param name="dest">Destination</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan<float> dest) public static unsafe void CopyTo(Block8x8F* blockPtr, Span<float> dest)
{ {
Marshal.Copy((IntPtr)blockPtr, dest.Data, dest.Offset, ScalarCount); blockPtr->CopyTo(dest);
} }
/// <summary> /// <summary>
@ -220,7 +221,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
/// Copy raw 32bit floating point data to dest /// Copy raw 32bit floating point data to dest
/// </summary> /// </summary>
/// <param name="dest">Destination</param> /// <param name="dest">Destination</param>
public unsafe void CopyTo(MutableSpan<int> dest) public unsafe void CopyTo(Span<int> dest)
{ {
fixed (Vector4* ptr = &this.V0L) fixed (Vector4* ptr = &this.V0L)
{ {

6
src/ImageSharp/Formats/Jpeg/GolangPort/Components/Decoder/OldJpegPixelArea.cs

@ -2,7 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F; using Block8x8F = SixLabors.ImageSharp.Formats.Jpeg.Common.Block8x8F;
@ -59,9 +59,9 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Components.Decoder
public int Offset { get; } public int Offset { get; }
/// <summary> /// <summary>
/// Gets a <see cref="MutableSpan{T}" /> of bytes to the pixel area /// Gets a <see cref="Span{T}" /> of bytes to the pixel area
/// </summary> /// </summary>
public MutableSpan<byte> Span => new MutableSpan<byte>(this.Pixels.Array, this.Offset); public Span<byte> Span => new Span<byte>(this.Pixels.Array, this.Offset);
/// <summary> /// <summary>
/// Returns the pixel at (x, y) /// Returns the pixel at (x, y)

98
src/ImageSharp/Formats/Jpeg/GolangPort/Utils/MutableSpan.cs

@ -1,98 +0,0 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
{
/// <summary>
/// Like corefxlab Span, but with an AddOffset() method for efficiency.
/// TODO: When Span will be official, consider replacing this class!
/// </summary>
/// <see>
/// <cref>https://github.com/dotnet/corefxlab/blob/master/src/System.Slices/System/Span.cs</cref>
/// </see>
/// <typeparam name="T">The type of the data in the span</typeparam>
internal struct MutableSpan<T>
{
/// <summary>
/// Data
/// </summary>
public T[] Data;
/// <summary>
/// Offset
/// </summary>
public int Offset;
/// <summary>
/// Initializes a new instance of the <see cref="MutableSpan{T}"/> struct.
/// </summary>
/// <param name="size">The size of the span</param>
/// <param name="offset">The offset (defaults to 0)</param>
public MutableSpan(int size, int offset = 0)
{
this.Data = new T[size];
this.Offset = offset;
}
/// <summary>
/// Initializes a new instance of the <see cref="MutableSpan{T}"/> struct.
/// </summary>
/// <param name="data">The data</param>
/// <param name="offset">The offset (defaults to 0)</param>
public MutableSpan(T[] data, int offset = 0)
{
this.Data = data;
this.Offset = offset;
}
/// <summary>
/// Gets the total count of data
/// </summary>
public int TotalCount => this.Data.Length - this.Offset;
/// <summary>
/// Index into the data
/// </summary>
/// <param name="idx">The data</param>
/// <returns>The value at the specified index</returns>
public T this[int idx]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return this.Data[idx + this.Offset];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set
{
this.Data[idx + this.Offset] = value;
}
}
public static implicit operator MutableSpan<T>(T[] data) => new MutableSpan<T>(data, 0);
/// <summary>
/// Slice the data
/// </summary>
/// <param name="offset">The offset</param>
/// <returns>The new <see cref="MutableSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MutableSpan<T> Slice(int offset)
{
return new MutableSpan<T>(this.Data, this.Offset + offset);
}
/// <summary>
/// Add to the offset
/// </summary>
/// <param name="offset">The additional offset</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddOffset(int offset)
{
this.Offset += offset;
}
}
}

61
src/ImageSharp/Formats/Jpeg/GolangPort/Utils/MutableSpanExtensions.cs

@ -6,27 +6,20 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
{ {
using System;
/// <summary> /// <summary>
/// MutableSpan Extensions /// MutableSpan Extensions
/// </summary> /// </summary>
internal static class MutableSpanExtensions internal static class MutableSpanExtensions
{ {
/// <summary>
/// Slice <see cref="MutableSpan{T}"/>
/// </summary>
/// <typeparam name="T">The type of the data in the span</typeparam>
/// <param name="array">The data array</param>
/// <param name="offset">The offset</param>
/// <returns>The new <see cref="MutableSpan{T}"/></returns>
public static MutableSpan<T> Slice<T>(this T[] array, int offset) => new MutableSpan<T>(array, offset);
/// <summary> /// <summary>
/// Save to a Vector4 /// Save to a Vector4
/// </summary> /// </summary>
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <param name="v">The vector to save to</param> /// <param name="v">The vector to save to</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SaveTo(this MutableSpan<float> data, ref Vector4 v) public static void SaveTo(this Span<float> data, ref Vector4 v)
{ {
v.X = data[0]; v.X = data[0];
v.Y = data[1]; v.Y = data[1];
@ -40,7 +33,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <param name="v">The vector to save to</param> /// <param name="v">The vector to save to</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void SaveTo(this MutableSpan<int> data, ref Vector4 v) public static void SaveTo(this Span<int> data, ref Vector4 v)
{ {
v.X = data[0]; v.X = data[0];
v.Y = data[1]; v.Y = data[1];
@ -54,7 +47,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <param name="v">The vector to load from</param> /// <param name="v">The vector to load from</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LoadFrom(this MutableSpan<float> data, ref Vector4 v) public static void LoadFrom(this Span<float> data, ref Vector4 v)
{ {
data[0] = v.X; data[0] = v.X;
data[1] = v.Y; data[1] = v.Y;
@ -68,7 +61,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
/// <param name="data">The data</param> /// <param name="data">The data</param>
/// <param name="v">The vector to load from</param> /// <param name="v">The vector to load from</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void LoadFrom(this MutableSpan<int> data, ref Vector4 v) public static void LoadFrom(this Span<int> data, ref Vector4 v)
{ {
data[0] = (int)v.X; data[0] = (int)v.X;
data[1] = (int)v.Y; data[1] = (int)v.Y;
@ -80,11 +73,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
/// Converts all int values of src to float /// Converts all int values of src to float
/// </summary> /// </summary>
/// <param name="src">Source</param> /// <param name="src">Source</param>
/// <returns>A new <see cref="MutableSpan{T}"/> with float values</returns> /// <returns>A new <see cref="Span{T}"/> with float values</returns>
public static MutableSpan<float> ConvertToFloat32MutableSpan(this MutableSpan<int> src) public static float[] ConvertAllToFloat(this int[] src)
{ {
MutableSpan<float> result = new MutableSpan<float>(src.TotalCount); float[] result = new float[src.Length];
for (int i = 0; i < src.TotalCount; i++) for (int i = 0; i < src.Length; i++)
{ {
result[i] = (float)src[i]; result[i] = (float)src[i];
} }
@ -96,11 +89,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
/// Converts all float values of src to int /// Converts all float values of src to int
/// </summary> /// </summary>
/// <param name="src">Source</param> /// <param name="src">Source</param>
/// <returns>A new <see cref="MutableSpan{T}"/> with float values</returns> /// <returns>A new <see cref="Span{T}"/> with float values</returns>
public static MutableSpan<int> ConvertToInt32MutableSpan(this MutableSpan<float> src) public static Span<int> ConvertToInt32MutableSpan(this Span<float> src)
{ {
MutableSpan<int> result = new MutableSpan<int>(src.TotalCount); int[] result = new int[src.Length];
for (int i = 0; i < src.TotalCount; i++) for (int i = 0; i < src.Length; i++)
{ {
result[i] = (int)src[i]; result[i] = (int)src[i];
} }
@ -113,11 +106,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
/// </summary> /// </summary>
/// <param name="src">The source</param> /// <param name="src">The source</param>
/// <param name="scalar">The scalar value to add</param> /// <param name="scalar">The scalar value to add</param>
/// <returns>A new instance of <see cref="MutableSpan{T}"/></returns> /// <returns>A new instance of <see cref="Span{T}"/></returns>
public static MutableSpan<float> AddScalarToAllValues(this MutableSpan<float> src, float scalar) public static Span<float> AddScalarToAllValues(this Span<float> src, float scalar)
{ {
MutableSpan<float> result = new MutableSpan<float>(src.TotalCount); float[] result = new float[src.Length];
for (int i = 0; i < src.TotalCount; i++) for (int i = 0; i < src.Length; i++)
{ {
result[i] = src[i] + scalar; result[i] = src[i] + scalar;
} }
@ -130,11 +123,11 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
/// </summary> /// </summary>
/// <param name="src">The source</param> /// <param name="src">The source</param>
/// <param name="scalar">The scalar value to add</param> /// <param name="scalar">The scalar value to add</param>
/// <returns>A new instance of <see cref="MutableSpan{T}"/></returns> /// <returns>A new instance of <see cref="Span{T}"/></returns>
public static MutableSpan<int> AddScalarToAllValues(this MutableSpan<int> src, int scalar) public static Span<int> AddScalarToAllValues(this Span<int> src, int scalar)
{ {
MutableSpan<int> result = new MutableSpan<int>(src.TotalCount); int[] result = new int[src.Length];
for (int i = 0; i < src.TotalCount; i++) for (int i = 0; i < src.Length; i++)
{ {
result[i] = src[i] + scalar; result[i] = src[i] + scalar;
} }
@ -143,15 +136,15 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils
} }
/// <summary> /// <summary>
/// Copy all values in src to a new <see cref="MutableSpan{T}"/> instance /// Copy all values in src to a new <see cref="Span{T}"/> instance
/// </summary> /// </summary>
/// <typeparam name="T">Element type</typeparam> /// <typeparam name="T">Element type</typeparam>
/// <param name="src">The source</param> /// <param name="src">The source</param>
/// <returns>A new instance of <see cref="MutableSpan{T}"/></returns> /// <returns>A new instance of <see cref="Span{T}"/></returns>
public static MutableSpan<T> Copy<T>(this MutableSpan<T> src) public static Span<T> Copy<T>(this Span<T> src)
{ {
MutableSpan<T> result = new MutableSpan<T>(src.TotalCount); T[] result = new T[src.Length];
for (int i = 0; i < src.TotalCount; i++) for (int i = 0; i < src.Length; i++)
{ {
result[i] = src[i]; result[i] = src[i];
} }

46
tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs

@ -128,7 +128,7 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(data, mirror); Assert.Equal(data, mirror);
// PrintLinearData((MutableSpan<float>)mirror); // PrintLinearData((Span<float>)mirror);
} }
[Fact] [Fact]
@ -153,7 +153,7 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(data, mirror); Assert.Equal(data, mirror);
// PrintLinearData((MutableSpan<float>)mirror); // PrintLinearData((Span<float>)mirror);
} }
[Fact] [Fact]
@ -178,7 +178,7 @@ namespace SixLabors.ImageSharp.Tests
Assert.Equal(data, mirror); Assert.Equal(data, mirror);
// PrintLinearData((MutableSpan<int>)mirror); // PrintLinearData((Span<int>)mirror);
} }
[Fact] [Fact]
@ -251,10 +251,10 @@ namespace SixLabors.ImageSharp.Tests
[Fact] [Fact]
public void iDCT2D8x4_RightPart() public void iDCT2D8x4_RightPart()
{ {
MutableSpan<float> sourceArray = Create8x8FloatData(); float[] sourceArray = Create8x8FloatData();
MutableSpan<float> expectedDestArray = new float[64]; float[] expectedDestArray = new float[64];
ReferenceImplementations.iDCT2D8x4_32f(sourceArray.Slice(4), expectedDestArray.Slice(4)); ReferenceImplementations.iDCT2D8x4_32f(sourceArray.AsSpan().Slice(4), expectedDestArray.AsSpan().Slice(4));
Block8x8F source = new Block8x8F(); Block8x8F source = new Block8x8F();
source.LoadFrom(sourceArray); source.LoadFrom(sourceArray);
@ -270,7 +270,7 @@ namespace SixLabors.ImageSharp.Tests
this.Output.WriteLine("**************"); this.Output.WriteLine("**************");
this.Print8x8Data(actualDestArray); this.Print8x8Data(actualDestArray);
Assert.Equal(expectedDestArray.Data, actualDestArray); Assert.Equal(expectedDestArray, actualDestArray);
} }
[Theory] [Theory]
@ -279,7 +279,7 @@ namespace SixLabors.ImageSharp.Tests
[InlineData(3)] [InlineData(3)]
public void TransformIDCT(int seed) public void TransformIDCT(int seed)
{ {
MutableSpan<float> sourceArray = Create8x8RandomFloatData(-200, 200, seed); Span<float> sourceArray = Create8x8RandomFloatData(-200, 200, seed);
float[] expectedDestArray = new float[64]; float[] expectedDestArray = new float[64];
float[] tempArray = new float[64]; float[] tempArray = new float[64];
@ -321,7 +321,7 @@ namespace SixLabors.ImageSharp.Tests
Block8x8F temp = new Block8x8F(); Block8x8F temp = new Block8x8F();
ReferenceImplementations.CopyColorsTo(ref block, new MutableSpan<byte>(colorsExpected, offset), stride); ReferenceImplementations.CopyColorsTo(ref block, new Span<byte>(colorsExpected, offset), stride);
block.CopyColorsTo(new Span<byte>(colorsActual, offset), stride, &temp); block.CopyColorsTo(new Span<byte>(colorsActual, offset), stride, &temp);
@ -372,21 +372,21 @@ namespace SixLabors.ImageSharp.Tests
[InlineData(2)] [InlineData(2)]
public void FDCT8x4_LeftPart(int seed) public void FDCT8x4_LeftPart(int seed)
{ {
MutableSpan<float> src = Create8x8RandomFloatData(-200, 200, seed); Span<float> src = Create8x8RandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F(); Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);
Block8x8F destBlock = new Block8x8F(); Block8x8F destBlock = new Block8x8F();
MutableSpan<float> expectedDest = new MutableSpan<float>(64); float[] expectedDest = new float[64];
ReferenceImplementations.fDCT2D8x4_32f(src, expectedDest); ReferenceImplementations.fDCT2D8x4_32f(src, expectedDest);
DCT.FDCT8x4_LeftPart(ref srcBlock, ref destBlock); DCT.FDCT8x4_LeftPart(ref srcBlock, ref destBlock);
MutableSpan<float> actualDest = new MutableSpan<float>(64); float[] actualDest = new float[64];
destBlock.CopyTo(actualDest); destBlock.CopyTo(actualDest);
Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f));
} }
[Theory] [Theory]
@ -394,21 +394,21 @@ namespace SixLabors.ImageSharp.Tests
[InlineData(2)] [InlineData(2)]
public void FDCT8x4_RightPart(int seed) public void FDCT8x4_RightPart(int seed)
{ {
MutableSpan<float> src = Create8x8RandomFloatData(-200, 200, seed); Span<float> src = Create8x8RandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F(); Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);
Block8x8F destBlock = new Block8x8F(); Block8x8F destBlock = new Block8x8F();
MutableSpan<float> expectedDest = new MutableSpan<float>(64); float[] expectedDest = new float[64];
ReferenceImplementations.fDCT2D8x4_32f(src.Slice(4), expectedDest.Slice(4)); ReferenceImplementations.fDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan().Slice(4));
DCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock); DCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock);
MutableSpan<float> actualDest = new MutableSpan<float>(64); float[] actualDest = new float[64];
destBlock.CopyTo(actualDest); destBlock.CopyTo(actualDest);
Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f));
} }
[Theory] [Theory]
@ -416,23 +416,23 @@ namespace SixLabors.ImageSharp.Tests
[InlineData(2)] [InlineData(2)]
public void TransformFDCT(int seed) public void TransformFDCT(int seed)
{ {
MutableSpan<float> src = Create8x8RandomFloatData(-200, 200, seed); Span<float> src = Create8x8RandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F(); Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src); srcBlock.LoadFrom(src);
Block8x8F destBlock = new Block8x8F(); Block8x8F destBlock = new Block8x8F();
MutableSpan<float> expectedDest = new MutableSpan<float>(64); float[] expectedDest = new float[64];
MutableSpan<float> temp1 = new MutableSpan<float>(64); float[] temp1 = new float[64];
Block8x8F temp2 = new Block8x8F(); Block8x8F temp2 = new Block8x8F();
ReferenceImplementations.fDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); ReferenceImplementations.fDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true);
DCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false); DCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false);
MutableSpan<float> actualDest = new MutableSpan<float>(64); float[] actualDest = new float[64];
destBlock.CopyTo(actualDest); destBlock.CopyTo(actualDest);
Assert.Equal(actualDest.Data, expectedDest.Data, new ApproximateFloatComparer(1f)); Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f));
} }
[Theory] [Theory]

14
tests/ImageSharp.Tests/Formats/Jpg/JpegUtilityTestFixture.cs

@ -60,12 +60,12 @@ namespace SixLabors.ImageSharp.Tests
return result; return result;
} }
internal static MutableSpan<float> Create8x8RandomFloatData(int minValue, int maxValue, int seed = 42) internal static float[] Create8x8RandomFloatData(int minValue, int maxValue, int seed = 42)
=> new MutableSpan<int>(Create8x8RandomIntData(minValue, maxValue, seed)).ConvertToFloat32MutableSpan(); => Create8x8RandomIntData(minValue, maxValue, seed).ConvertAllToFloat();
internal void Print8x8Data<T>(MutableSpan<T> data) => this.Print8x8Data(data.Data); internal void Print8x8Data<T>(T[] data) => this.Print8x8Data(new Span<T>(data));
internal void Print8x8Data<T>(T[] data) internal void Print8x8Data<T>(Span<T> data)
{ {
StringBuilder bld = new StringBuilder(); StringBuilder bld = new StringBuilder();
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
@ -80,11 +80,11 @@ namespace SixLabors.ImageSharp.Tests
this.Output.WriteLine(bld.ToString()); this.Output.WriteLine(bld.ToString());
} }
internal void PrintLinearData<T>(T[] data) => this.PrintLinearData(new MutableSpan<T>(data), data.Length); internal void PrintLinearData<T>(T[] data) => this.PrintLinearData(new Span<T>(data), data.Length);
internal void PrintLinearData<T>(MutableSpan<T> data, int count = -1) internal void PrintLinearData<T>(Span<T> data, int count = -1)
{ {
if (count < 0) count = data.TotalCount; if (count < 0) count = data.Length;
StringBuilder bld = new StringBuilder(); StringBuilder bld = new StringBuilder();
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)

66
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementations.cs

@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests
/// Transpose 8x8 block stored linearly in a <see cref="MutableSpan{T}"/> (inplace) /// Transpose 8x8 block stored linearly in a <see cref="MutableSpan{T}"/> (inplace)
/// </summary> /// </summary>
/// <param name="data"></param> /// <param name="data"></param>
internal static void Transpose8x8(MutableSpan<float> data) internal static void Transpose8x8(Span<float> data)
{ {
for (int i = 1; i < 8; i++) for (int i = 1; i < 8; i++)
{ {
@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests
/// <summary> /// <summary>
/// Transpose 8x8 block stored linearly in a <see cref="MutableSpan{T}"/> /// Transpose 8x8 block stored linearly in a <see cref="MutableSpan{T}"/>
/// </summary> /// </summary>
internal static void Transpose8x8(MutableSpan<float> src, MutableSpan<float> dest) internal static void Transpose8x8(Span<float> src, Span<float> dest)
{ {
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests
/// Leave results scaled up by an overall factor of 8. /// Leave results scaled up by an overall factor of 8.
/// </summary> /// </summary>
/// <param name="block">The block of coefficients.</param> /// <param name="block">The block of coefficients.</param>
public static void TransformFDCTInplace(MutableSpan<int> block) public static void TransformFDCTInplace(Span<int> block)
{ {
// Pass 1: process rows. // Pass 1: process rows.
for (int y = 0; y < 8; y++) for (int y = 0; y < 8; y++)
@ -231,7 +231,7 @@ namespace SixLabors.ImageSharp.Tests
/// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984. /// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
/// </summary> /// </summary>
/// <param name="src">The source block of coefficients</param> /// <param name="src">The source block of coefficients</param>
public static void TransformIDCTInplace(MutableSpan<int> src) public static void TransformIDCTInplace(Span<int> src)
{ {
// Horizontal 1-D IDCT. // Horizontal 1-D IDCT.
for (int y = 0; y < 8; y++) for (int y = 0; y < 8; y++)
@ -364,7 +364,7 @@ namespace SixLabors.ImageSharp.Tests
/// </summary> /// </summary>
/// <param name="y"></param> /// <param name="y"></param>
/// <param name="x"></param> /// <param name="x"></param>
private static void iDCT1Dllm_32f(MutableSpan<float> y, MutableSpan<float> x) private static void iDCT1Dllm_32f(Span<float> y, Span<float> x)
{ {
float a0, a1, a2, a3, b0, b1, b2, b3; float a0, a1, a2, a3, b0, b1, b2, b3;
float z0, z1, z2, z3, z4; float z0, z1, z2, z3, z4;
@ -421,7 +421,7 @@ namespace SixLabors.ImageSharp.Tests
/// <param name="s"></param> /// <param name="s"></param>
/// <param name="d"></param> /// <param name="d"></param>
/// <param name="temp"></param> /// <param name="temp"></param>
internal static void iDCT2D_llm(MutableSpan<float> s, MutableSpan<float> d, MutableSpan<float> temp) internal static void iDCT2D_llm(Span<float> s, Span<float> d, Span<float> temp)
{ {
int j; int j;
@ -453,7 +453,7 @@ namespace SixLabors.ImageSharp.Tests
/// </summary> /// </summary>
/// <param name="s">Source</param> /// <param name="s">Source</param>
/// <param name="d">Destination</param> /// <param name="d">Destination</param>
public static void fDCT2D8x4_32f(MutableSpan<float> s, MutableSpan<float> d) public static void fDCT2D8x4_32f(Span<float> s, Span<float> d)
{ {
Vector4 c0 = _mm_load_ps(s, 0); Vector4 c0 = _mm_load_ps(s, 0);
Vector4 c1 = _mm_load_ps(s, 56); Vector4 c1 = _mm_load_ps(s, 56);
@ -550,7 +550,7 @@ namespace SixLabors.ImageSharp.Tests
}*/ }*/
} }
public static void fDCT8x8_llm_sse(MutableSpan<float> s, MutableSpan<float> d, MutableSpan<float> temp) public static void fDCT8x8_llm_sse(Span<float> s, Span<float> d, Span<float> temp)
{ {
Transpose8x8(s, temp); Transpose8x8(s, temp);
@ -566,33 +566,33 @@ namespace SixLabors.ImageSharp.Tests
Vector4 c = new Vector4(0.1250f); Vector4 c = new Vector4(0.1250f);
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//0 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//0
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//1 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//1
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//2 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//2
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//3 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//3
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//4 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//4
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//5 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//5
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//6 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//6
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//7 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//7
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//8 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//8
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//9 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//9
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//10 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//10
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//11 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//11
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//12 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//12
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//13 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//13
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//14 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//14
_mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d.AddOffset(4);//15 _mm_store_ps(d, 0, (_mm_load_ps(d, 0) * c)); d = d.Slice(4);//15
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 _mm_load_ps(MutableSpan<float> src, int offset) private static Vector4 _mm_load_ps(Span<float> src, int offset)
{ {
src = src.Slice(offset); src = src.Slice(offset);
return new Vector4(src[0], src[1], src[2], src[3]); return new Vector4(src[0], src[1], src[2], src[3]);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void _mm_store_ps(MutableSpan<float> dest, int offset, Vector4 src) private static void _mm_store_ps(Span<float> dest, int offset, Vector4 src)
{ {
dest = dest.Slice(offset); dest = dest.Slice(offset);
dest[0] = src.X; dest[0] = src.X;
@ -632,7 +632,7 @@ namespace SixLabors.ImageSharp.Tests
/// </summary> /// </summary>
/// <param name="y"></param> /// <param name="y"></param>
/// <param name="x"></param> /// <param name="x"></param>
internal static void iDCT2D8x4_32f(MutableSpan<float> y, MutableSpan<float> x) internal static void iDCT2D8x4_32f(Span<float> y, Span<float> x)
{ {
/* /*
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i;
@ -750,7 +750,7 @@ namespace SixLabors.ImageSharp.Tests
/// <param name="block"></param> /// <param name="block"></param>
/// <param name="buffer"></param> /// <param name="buffer"></param>
/// <param name="stride"></param> /// <param name="stride"></param>
internal static unsafe void CopyColorsTo(ref Block8x8F block, MutableSpan<byte> buffer, int stride) internal static unsafe void CopyColorsTo(ref Block8x8F block, Span<byte> buffer, int stride)
{ {
fixed (Block8x8F* p = &block) fixed (Block8x8F* p = &block)
{ {
@ -784,7 +784,7 @@ namespace SixLabors.ImageSharp.Tests
} }
} }
internal static void fDCT1Dllm_32f(MutableSpan<float> x, MutableSpan<float> y) internal static void fDCT1Dllm_32f(Span<float> x, Span<float> y)
{ {
float t0, t1, t2, t3, t4, t5, t6, t7; float t0, t1, t2, t3, t4, t5, t6, t7;
float c0, c1, c2, c3; float c0, c1, c2, c3;
@ -844,13 +844,13 @@ namespace SixLabors.ImageSharp.Tests
} }
internal static void fDCT2D_llm( internal static void fDCT2D_llm(
MutableSpan<float> s, Span<float> s,
MutableSpan<float> d, Span<float> d,
MutableSpan<float> temp, Span<float> temp,
bool downscaleBy8 = false, bool downscaleBy8 = false,
bool offsetSourceByNeg128 = false) bool offsetSourceByNeg128 = false)
{ {
MutableSpan<float> sWorker = offsetSourceByNeg128 ? s.AddScalarToAllValues(-128f) : s; Span<float> sWorker = offsetSourceByNeg128 ? s.AddScalarToAllValues(-128f) : s;
for (int j = 0; j < 8; j++) for (int j = 0; j < 8; j++)
{ {

28
tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs

@ -9,6 +9,8 @@ using Xunit.Abstractions;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{ {
using System;
public class ReferenceImplementationsTests : JpegUtilityTestFixture public class ReferenceImplementationsTests : JpegUtilityTestFixture
{ {
public ReferenceImplementationsTests(ITestOutputHelper output) public ReferenceImplementationsTests(ITestOutputHelper output)
@ -23,13 +25,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)] [InlineData(2)]
public void Idct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) public void Idct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed)
{ {
MutableSpan<int> intData = Create8x8RandomIntData(-200, 200, seed); int[] intData = Create8x8RandomIntData(-200, 200, seed);
MutableSpan<float> floatSrc = intData.ConvertToFloat32MutableSpan(); Span<float> floatSrc = intData.ConvertAllToFloat();
ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(intData); ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(intData);
MutableSpan<float> dest = new MutableSpan<float>(64); float[] dest = new float[64];
MutableSpan<float> temp = new MutableSpan<float>(64); float[] temp = new float[64];
ReferenceImplementations.iDCT2D_llm(floatSrc, dest, temp); ReferenceImplementations.iDCT2D_llm(floatSrc, dest, temp);
@ -48,9 +50,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2, 0)] [InlineData(2, 0)]
public void IntegerDCT_ForwardThenInverse(int seed, int startAt) public void IntegerDCT_ForwardThenInverse(int seed, int startAt)
{ {
MutableSpan<int> original = Create8x8RandomIntData(-200, 200, seed); Span<int> original = Create8x8RandomIntData(-200, 200, seed);
MutableSpan<int> block = original.AddScalarToAllValues(128); Span<int> block = original.AddScalarToAllValues(128);
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(block); ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(block);
@ -78,9 +80,9 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public void FloatingPointDCT_ReferenceImplementation_ForwardThenInverse(int seed, int startAt) public void FloatingPointDCT_ReferenceImplementation_ForwardThenInverse(int seed, int startAt)
{ {
int[] data = Create8x8RandomIntData(-200, 200, seed); int[] data = Create8x8RandomIntData(-200, 200, seed);
MutableSpan<float> src = new MutableSpan<int>(data).ConvertToFloat32MutableSpan(); float[] src = data.ConvertAllToFloat();
MutableSpan<float> dest = new MutableSpan<float>(64); float[] dest = new float[64];
MutableSpan<float> temp = new MutableSpan<float>(64); float[] temp = new float[64];
ReferenceImplementations.fDCT2D_llm(src, dest, temp, true); ReferenceImplementations.fDCT2D_llm(src, dest, temp, true);
ReferenceImplementations.iDCT2D_llm(dest, src, temp); ReferenceImplementations.iDCT2D_llm(dest, src, temp);
@ -100,13 +102,13 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)] [InlineData(2)]
public void Fdct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) public void Fdct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed)
{ {
MutableSpan<int> intData = Create8x8RandomIntData(-200, 200, seed); int[] intData = Create8x8RandomIntData(-200, 200, seed);
MutableSpan<float> floatSrc = intData.ConvertToFloat32MutableSpan(); float[] floatSrc = intData.ConvertAllToFloat();
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(intData); ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(intData);
MutableSpan<float> dest = new MutableSpan<float>(64); float[] dest = new float[64];
MutableSpan<float> temp = new MutableSpan<float>(64); float[] temp = new float[64];
ReferenceImplementations.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true); ReferenceImplementations.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true);

Loading…
Cancel
Save