diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
index 79b26a042..adfabc13c 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
@@ -2,9 +2,11 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
using System.Text;
namespace SixLabors.ImageSharp.Formats.Jpeg.Components
@@ -276,6 +278,64 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
///
public override int GetHashCode() => (this[0] * 31) + this[1];
+ ///
+ /// Returns index of the last non-zero element in given matrix.
+ ///
+ ///
+ /// Returns 0 for all-zero matrix by convention.
+ ///
+ /// Index of the last non-zero element.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public int GetLastValuableElementIndex()
+ {
+#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(Unsafe.Add(ref mcuStride, i).AsInt32(), zero8).AsByte());
+
+ if (areEqual != equalityMask)
+ {
+ // Each 2 bits represents comparison operation for each 2-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 2 bits in the mask, we need to divide lzcnt result by 2
+ // to get the exact number of zero elements in the stride
+ int strideRelativeIndex = 7 - (lzcnt / 2);
+ return (i * 8) + strideRelativeIndex;
+ }
+ }
+
+ return 0;
+ }
+ else
+#endif
+ {
+ int index = Size - 1;
+ ref short elemRef = ref Unsafe.As(ref this);
+
+ while (index > 0 && Unsafe.Add(ref elemRef, index) == 0)
+ {
+ index--;
+ }
+
+ return index;
+ }
+ }
+
///
/// Calculate the total sum of absolute differences of elements in 'a' and 'b'.
///
diff --git a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
index a11b807bb..b0d7b0876 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
@@ -864,5 +864,60 @@ 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 GetLastValuableElementIndex()
+ {
+#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/Encoder/HuffmanScanEncoder.cs b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
index 373475f6b..134b4e1cc 100644
--- a/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
+++ b/src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
@@ -276,7 +276,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
int[] acHuffTable = this.huffmanTables[(2 * (int)index) + 1].Values;
int runLength = 0;
- int lastValuableIndex = GetLastValuableElementIndex(ref refTemp2);
+ int lastValuableIndex = refTemp2.GetLastValuableElementIndex();
for (int zig = 1; zig <= lastValuableIndex; zig++)
{
int ac = (int)refTemp2[zig];
@@ -458,65 +458,6 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder
#endif
}
- ///
- /// Returns index of the last non-zero element in given matrix.
- ///
- ///
- /// Returns 0 for all-zero matrix by convention.
- ///
- /// Mcu block.
- /// Index of the last non-zero element.
- [MethodImpl(InliningOptions.ShortMethod)]
- internal static int GetLastValuableElementIndex(ref Block8x8F mcu)
- {
-#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 mcu.V0;
-
- 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 0;
- }
- else
-#endif
- {
- int index = Block8x8F.Size - 1;
- ref float elemRef = ref Unsafe.As(ref mcu);
-
- while (index > 0 && (int)Unsafe.Add(ref elemRef, index) == 0)
- {
- index--;
- }
-
- return index;
- }
- }
-
[MethodImpl(InliningOptions.ShortMethod)]
private void WriteToStream()
{
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs
index b953e80b8..f75b0a0b8 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs
@@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int expectedLessThan = 1;
- int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data);
+ int actual = data.GetLastValuableElementIndex();
Assert.True(actual < expectedLessThan);
}
@@ -118,7 +118,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int expected = Block8x8F.Size - 1;
- int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data);
+ int actual = data.GetLastValuableElementIndex();
Assert.Equal(expected, actual);
}
@@ -147,7 +147,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int expected = setIndex;
- int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data);
+ int actual = data.GetLastValuableElementIndex();
Assert.Equal(expected, actual);
}
@@ -182,7 +182,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int expected = lastIndex;
- int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data);
+ int actual = data.GetLastValuableElementIndex();
Assert.Equal(expected, actual);
}
@@ -226,7 +226,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
int expected = lastIndex2;
- int actual = HuffmanScanEncoder.GetLastValuableElementIndex(ref data);
+ int actual = data.GetLastValuableElementIndex();
Assert.Equal(expected, actual);
}