diff --git a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs index afcf4158be..ad2e290f6f 100644 --- a/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs +++ b/src/ImageSharp/Formats/Jpeg/Components/FastFloatingPointDCT.cs @@ -203,7 +203,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components /// /// Source /// Destination - private static void FDCT8x8_Avx(ref Block8x8F s, ref Block8x8F d) + public static void FDCT8x8_Avx(ref Block8x8F s, ref Block8x8F d) { #if SUPPORTS_RUNTIME_INTRINSICS Debug.Assert(Avx.IsSupported, "AVX is required to execute this method"); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs index 75ad5427c7..99dce57c70 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; - +using System.Runtime.Intrinsics.X86; using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; @@ -22,94 +22,160 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg { } - [Fact] - public void IDCT2D8x4_LeftPart() + // Reference tests + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void LLM_TransformIDCT_CompareToNonOptimized(int seed) { - float[] sourceArray = Create8x8FloatData(); - var expectedDestArray = new float[64]; + float[] sourceArray = Create8x8RoundedRandomFloatData(-1000, 1000, seed); - ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(sourceArray, expectedDestArray); + var source = Block8x8F.Load(sourceArray); - var source = default(Block8x8F); - source.LoadFrom(sourceArray); + Block8x8F expected = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref source); - var dest = default(Block8x8F); + var temp = default(Block8x8F); + var actual = default(Block8x8F); + FastFloatingPointDCT.TransformIDCT(ref source, ref actual, ref temp); - FastFloatingPointDCT.IDCT8x4_LeftPart(ref source, ref dest); + this.CompareBlocks(expected, actual, 1f); + } - var actualDestArray = new float[64]; - dest.ScaledCopyTo(actualDestArray); + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(3)] + public void LLM_TransformIDCT_CompareToAccurate(int seed) + { + float[] sourceArray = Create8x8RoundedRandomFloatData(-1000, 1000, seed); + + var source = Block8x8F.Load(sourceArray); + + Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); - this.Print8x8Data(expectedDestArray); - this.Output.WriteLine("**************"); - this.Print8x8Data(actualDestArray); + var temp = default(Block8x8F); + var actual = default(Block8x8F); + FastFloatingPointDCT.TransformIDCT(ref source, ref actual, ref temp); - Assert.Equal(expectedDestArray, actualDestArray); + this.CompareBlocks(expected, actual, 1f); } - [Fact] - public void IDCT2D8x4_RightPart() + + // Inverse transform + [Theory] + [InlineData(1)] + [InlineData(2)] + public void IDCT8x4_LeftPart(int seed) { - float[] sourceArray = Create8x8FloatData(); - var expectedDestArray = new float[64]; + Span src = Create8x8RoundedRandomFloatData(-200, 200, seed); + var srcBlock = default(Block8x8F); + srcBlock.LoadFrom(src); + + var destBlock = default(Block8x8F); + + var expectedDest = new float[64]; + + // reference + ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(src, expectedDest); - ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(sourceArray.AsSpan(4), expectedDestArray.AsSpan(4)); + // testee + FastFloatingPointDCT.IDCT8x4_LeftPart(ref srcBlock, ref destBlock); + + var actualDest = new float[64]; + destBlock.ScaledCopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void IDCT8x4_RightPart(int seed) + { + Span src = Create8x8RoundedRandomFloatData(-200, 200, seed); + var srcBlock = default(Block8x8F); + srcBlock.LoadFrom(src); - var source = default(Block8x8F); - source.LoadFrom(sourceArray); + var destBlock = default(Block8x8F); - var dest = default(Block8x8F); + var expectedDest = new float[64]; - FastFloatingPointDCT.IDCT8x4_RightPart(ref source, ref dest); + // reference + ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan(4)); - var actualDestArray = new float[64]; - dest.ScaledCopyTo(actualDestArray); + // testee + FastFloatingPointDCT.IDCT8x4_RightPart(ref srcBlock, ref destBlock); - this.Print8x8Data(expectedDestArray); - this.Output.WriteLine("**************"); - this.Print8x8Data(actualDestArray); + var actualDest = new float[64]; + destBlock.ScaledCopyTo(actualDest); - Assert.Equal(expectedDestArray, actualDestArray); + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); } [Theory] [InlineData(1)] [InlineData(2)] - [InlineData(3)] - public void LLM_TransformIDCT_CompareToNonOptimized(int seed) + public void IDCT8x8_Avx(int seed) { - float[] sourceArray = Create8x8RoundedRandomFloatData(-1000, 1000, seed); + if (!Avx.IsSupported) + { + this.Output.WriteLine("No AVX present, skipping test!"); + return; + } - var source = Block8x8F.Load(sourceArray); + Span src = Create8x8RoundedRandomFloatData(-200, 200, seed); + var srcBlock = default(Block8x8F); + srcBlock.LoadFrom(src); - Block8x8F expected = ReferenceImplementations.LLM_FloatingPoint_DCT.TransformIDCT(ref source); + var destBlock = default(Block8x8F); - var temp = default(Block8x8F); - var actual = default(Block8x8F); - FastFloatingPointDCT.TransformIDCT(ref source, ref actual, ref temp); + var expectedDest = new float[64]; - this.CompareBlocks(expected, actual, 1f); + // reference, left part + ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(src, expectedDest); + + // reference, right part + ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan(4)); + + // testee, whole 8x8 + FastFloatingPointDCT.IDCT8x8_Avx(ref srcBlock, ref destBlock); + + var actualDest = new float[64]; + destBlock.ScaledCopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); } [Theory] [InlineData(1)] [InlineData(2)] - [InlineData(3)] - public void LLM_TransformIDCT_CompareToAccurate(int seed) + public void TransformIDCT(int seed) { - float[] sourceArray = Create8x8RoundedRandomFloatData(-1000, 1000, seed); + Span src = Create8x8RoundedRandomFloatData(-200, 200, seed); + var srcBlock = default(Block8x8F); + srcBlock.LoadFrom(src); - var source = Block8x8F.Load(sourceArray); + var destBlock = default(Block8x8F); - Block8x8F expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source); + var expectedDest = new float[64]; + var temp1 = new float[64]; + var temp2 = default(Block8x8F); - var temp = default(Block8x8F); - var actual = default(Block8x8F); - FastFloatingPointDCT.TransformIDCT(ref source, ref actual, ref temp); + // reference + ReferenceImplementations.LLM_FloatingPoint_DCT.IDCT2D_llm(src, expectedDest, temp1); - this.CompareBlocks(expected, actual, 1f); + // testee + FastFloatingPointDCT.TransformIDCT(ref srcBlock, ref destBlock, ref temp2); + + var actualDest = new float[64]; + destBlock.ScaledCopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); } + + // Forward transform [Theory] [InlineData(1)] [InlineData(2)] @@ -123,7 +189,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var expectedDest = new float[64]; + // reference ReferenceImplementations.LLM_FloatingPoint_DCT.FDCT2D8x4_32f(src, expectedDest); + + // testee FastFloatingPointDCT.FDCT8x4_LeftPart(ref srcBlock, ref destBlock); var actualDest = new float[64]; @@ -145,7 +214,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var expectedDest = new float[64]; + // reference ReferenceImplementations.LLM_FloatingPoint_DCT.FDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan(4)); + + // testee FastFloatingPointDCT.FDCT8x4_RightPart(ref srcBlock, ref destBlock); var actualDest = new float[64]; @@ -154,6 +226,40 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); } + [Theory] + [InlineData(1)] + [InlineData(2)] + public void FDCT8x8_Avx(int seed) + { + if (!Avx.IsSupported) + { + this.Output.WriteLine("No AVX present, skipping test!"); + return; + } + + Span src = Create8x8RoundedRandomFloatData(-200, 200, seed); + var srcBlock = default(Block8x8F); + srcBlock.LoadFrom(src); + + var destBlock = default(Block8x8F); + + var expectedDest = new float[64]; + + // reference, left part + ReferenceImplementations.LLM_FloatingPoint_DCT.FDCT2D8x4_32f(src, expectedDest); + + // reference, right part + ReferenceImplementations.LLM_FloatingPoint_DCT.FDCT2D8x4_32f(src.Slice(4), expectedDest.AsSpan(4)); + + // testee, whole 8x8 + FastFloatingPointDCT.FDCT8x8_Avx(ref srcBlock, ref destBlock); + + var actualDest = new float[64]; + destBlock.ScaledCopyTo(actualDest); + + Assert.Equal(actualDest, expectedDest, new ApproximateFloatComparer(1f)); + } + [Theory] [InlineData(1)] [InlineData(2)] @@ -169,7 +275,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var temp1 = new float[64]; var temp2 = default(Block8x8F); + // reference ReferenceImplementations.LLM_FloatingPoint_DCT.FDCT2D_llm(src, expectedDest, temp1, downscaleBy8: true); + + // testee FastFloatingPointDCT.TransformFDCT(ref srcBlock, ref destBlock, ref temp2, false); var actualDest = new float[64];