diff --git a/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs index 095fdef812..b953e80b80 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs @@ -2,12 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -#if SUPPORTS_RUNTIME_INTRINSICS -using System.Runtime.Intrinsics.X86; -#endif using SixLabors.ImageSharp.Formats.Jpeg.Components; using SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder; -using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils; using SixLabors.ImageSharp.Tests.TestUtilities; using Xunit; using Xunit.Abstractions; @@ -22,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg public HuffmanScanEncoderTests(ITestOutputHelper output) { - Output = output; + this.Output = output; } private static int GetHuffmanEncodingLength_Reference(uint number) @@ -33,25 +29,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg number >>= 16; bits += 16; } + if (number > 127) { number >>= 8; bits += 8; } + if (number > 7) { number >>= 4; bits += 4; } + if (number > 1) { number >>= 2; bits += 2; } + if (number > 0) { bits++; } + return bits; } @@ -84,5 +85,157 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg Assert.Equal(expected, actual); } } + + [Fact] + public void GetLastValuableElementIndex_AllZero() + { + static void RunTest() + { + Block8x8F data = default; + + int expectedLessThan = 1; + + int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data); + + Assert.True(actual < expectedLessThan); + } + + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); + } + + [Fact] + public void GetLastValuableElementIndex_AllNonZero() + { + static void RunTest() + { + Block8x8F data = default; + for (int i = 0; i < Block8x8F.Size; i++) + { + data[i] = 10; + } + + int expected = Block8x8F.Size - 1; + + int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data); + + Assert.Equal(expected, actual); + } + + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void GetLastValuableElementIndex_RandomFilledSingle(int seed) + { + static void RunTest(string seedSerialized) + { + int seed = FeatureTestRunner.Deserialize(seedSerialized); + var rng = new Random(seed); + + for (int i = 0; i < 1000; i++) + { + Block8x8F data = default; + + int setIndex = rng.Next(1, Block8x8F.Size); + data[setIndex] = rng.Next(); + + int expected = setIndex; + + int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data); + + Assert.Equal(expected, actual); + } + } + + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + seed, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void GetLastValuableElementIndex_RandomFilledPartially(int seed) + { + static void RunTest(string seedSerialized) + { + int seed = FeatureTestRunner.Deserialize(seedSerialized); + var rng = new Random(seed); + + for (int i = 0; i < 1000; i++) + { + Block8x8F data = default; + + int lastIndex = rng.Next(1, Block8x8F.Size); + int fillValue = rng.Next(); + for (int dataIndex = 0; dataIndex <= lastIndex; dataIndex++) + { + data[dataIndex] = fillValue; + } + + int expected = lastIndex; + + int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data); + + Assert.Equal(expected, actual); + } + } + + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + seed, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + public void GetLastValuableElementIndex_RandomFilledFragmented(int seed) + { + static void RunTest(string seedSerialized) + { + int seed = FeatureTestRunner.Deserialize(seedSerialized); + var rng = new Random(seed); + + for (int i = 0; i < 1000; i++) + { + Block8x8F data = default; + + int fillValue = rng.Next(); + + // first filled chunk + int lastIndex1 = rng.Next(1, Block8x8F.Size / 2); + for (int dataIndex = 0; dataIndex <= lastIndex1; dataIndex++) + { + data[dataIndex] = fillValue; + } + + // second filled chunk, there might be a spot with zero(s) between first and second chunk + int lastIndex2 = rng.Next(lastIndex1 + 1, Block8x8F.Size); + for (int dataIndex = 0; dataIndex <= lastIndex2; dataIndex++) + { + data[dataIndex] = fillValue; + } + + int expected = lastIndex2; + + int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data); + + Assert.Equal(expected, actual); + } + } + + FeatureTestRunner.RunWithHwIntrinsicsFeature( + RunTest, + seed, + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2); + } } }