diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index 79a35e2cd..b29c13e6e 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -768,62 +768,5 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return true;
}
}
-
- ///
- /// Returns index of the last non-zero element in this matrix.
- ///
- ///
- /// Index of the last non-zero element. Returns -1 if all elements are equal to zero.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public int GetLastNonZeroIndex()
- {
-#if SUPPORTS_RUNTIME_INTRINSICS
- if (Avx2.IsSupported)
- {
- const int equalityMask = unchecked((int)0b1111_1111_1111_1111_1111_1111_1111_1111);
-
- Vector256 zero8 = Vector256.Zero;
-
- ref Vector256 mcuStride = ref Unsafe.As>(ref this);
-
- for (int i = 7; i >= 0; i--)
- {
- int areEqual = Avx2.MoveMask(Avx2.CompareEqual(Avx.ConvertToVector256Int32WithTruncation(Unsafe.Add(ref mcuStride, i)), zero8).AsByte());
-
- if (areEqual != equalityMask)
- {
- // Each 4 bits represents comparison operation for each 4-byte element in input vectors
- // LSB represents first element in the stride
- // MSB represents last element in the stride
- // lzcnt operation would calculate number of zero numbers at the end
-
- // Given mask is not actually suitable for lzcnt as 1's represent zero elements and 0's represent non-zero elements
- // So we need to invert it
- int lzcnt = BitOperations.LeadingZeroCount(~(uint)areEqual);
-
- // As input number is represented by 4 bits in the mask, we need to divide lzcnt result by 4
- // to get the exact number of zero elements in the stride
- int strideRelativeIndex = 7 - (lzcnt / 4);
- return (i * 8) + strideRelativeIndex;
- }
- }
-
- return -1;
- }
- else
-#endif
- {
- int index = Size - 1;
- ref float elemRef = ref Unsafe.As(ref this);
-
- while (index >= 0 && (int)Unsafe.Add(ref elemRef, index) == 0)
- {
- index--;
- }
-
- return index;
- }
- }
}
}
diff --git a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
index c2b0fc5d0..e519a8a1d 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/ZigZag.cs
@@ -2,10 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Runtime.Intrinsics;
-using System.Runtime.Intrinsics.X86;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
{
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
index afe71ad04..6d73181cb 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
@@ -1,9 +1,10 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
+using System;
using SixLabors.ImageSharp.Formats.Jpeg.Components;
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
-
+using SixLabors.ImageSharp.Tests.TestUtilities;
using Xunit;
using Xunit.Abstractions;
@@ -121,5 +122,157 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.Equal(15, d);
}
+
+ [Fact]
+ public void GetLastNonZeroIndex_AllZero()
+ {
+ static void RunTest()
+ {
+ Block8x8 data = default;
+
+ int expected = -1;
+
+ int actual = data.GetLastNonZeroIndex();
+
+ Assert.Equal(expected, actual);
+ }
+
+ FeatureTestRunner.RunWithHwIntrinsicsFeature(
+ RunTest,
+ HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
+ }
+
+ [Fact]
+ public void GetLastNonZeroIndex_AllNonZero()
+ {
+ static void RunTest()
+ {
+ Block8x8 data = default;
+ for (int i = 0; i < Block8x8.Size; i++)
+ {
+ data[i] = 10;
+ }
+
+ int expected = Block8x8.Size - 1;
+
+ int actual = data.GetLastNonZeroIndex();
+
+ Assert.Equal(expected, actual);
+ }
+
+ FeatureTestRunner.RunWithHwIntrinsicsFeature(
+ RunTest,
+ HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void GetLastNonZeroIndex_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++)
+ {
+ Block8x8 data = default;
+
+ int setIndex = rng.Next(1, Block8x8.Size);
+ data[setIndex] = (short)rng.Next(-2000, 2000);
+
+ int expected = setIndex;
+
+ int actual = data.GetLastNonZeroIndex();
+
+ Assert.Equal(expected, actual);
+ }
+ }
+
+ FeatureTestRunner.RunWithHwIntrinsicsFeature(
+ RunTest,
+ seed,
+ HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void GetLastNonZeroIndex_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++)
+ {
+ Block8x8 data = default;
+
+ int lastIndex = rng.Next(1, Block8x8.Size);
+ short fillValue = (short)rng.Next(-2000, 2000);
+ for (int dataIndex = 0; dataIndex <= lastIndex; dataIndex++)
+ {
+ data[dataIndex] = fillValue;
+ }
+
+ int expected = lastIndex;
+
+ int actual = data.GetLastNonZeroIndex();
+
+ Assert.Equal(expected, actual);
+ }
+ }
+
+ FeatureTestRunner.RunWithHwIntrinsicsFeature(
+ RunTest,
+ seed,
+ HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void GetLastNonZeroIndex_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++)
+ {
+ Block8x8 data = default;
+
+ short fillValue = (short)rng.Next(-2000, 2000);
+
+ // 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 = data.GetLastNonZeroIndex();
+
+ Assert.Equal(expected, actual);
+ }
+ }
+
+ FeatureTestRunner.RunWithHwIntrinsicsFeature(
+ RunTest,
+ seed,
+ HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
+ }
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs
index a3aa957ee..42f2fa0d5 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs
@@ -85,157 +85,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
Assert.Equal(expected, actual);
}
}
-
- [Fact]
- public void GetLastNonZeroIndex_AllZero()
- {
- static void RunTest()
- {
- Block8x8F data = default;
-
- int expectedLessThan = 1;
-
- int actual = data.GetLastNonZeroIndex();
-
- Assert.True(actual < expectedLessThan);
- }
-
- FeatureTestRunner.RunWithHwIntrinsicsFeature(
- RunTest,
- HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
- }
-
- [Fact]
- public void GetLastNonZeroIndex_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 = data.GetLastNonZeroIndex();
-
- Assert.Equal(expected, actual);
- }
-
- FeatureTestRunner.RunWithHwIntrinsicsFeature(
- RunTest,
- HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
- }
-
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- public void GetLastNonZeroIndex_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 = data.GetLastNonZeroIndex();
-
- Assert.Equal(expected, actual);
- }
- }
-
- FeatureTestRunner.RunWithHwIntrinsicsFeature(
- RunTest,
- seed,
- HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
- }
-
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- public void GetLastNonZeroIndex_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 = data.GetLastNonZeroIndex();
-
- Assert.Equal(expected, actual);
- }
- }
-
- FeatureTestRunner.RunWithHwIntrinsicsFeature(
- RunTest,
- seed,
- HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
- }
-
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- public void GetLastNonZeroIndex_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 = data.GetLastNonZeroIndex();
-
- Assert.Equal(expected, actual);
- }
- }
-
- FeatureTestRunner.RunWithHwIntrinsicsFeature(
- RunTest,
- seed,
- HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX2);
- }
}
}