Browse Source

8x8 matrices small fixes

pull/1761/head
Dmitry Pentin 5 years ago
parent
commit
2bccda8c03
  1. 60
      src/ImageSharp/Formats/Jpeg/Components/Block8x8.cs
  2. 55
      src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs
  3. 61
      src/ImageSharp/Formats/Jpeg/Components/Encoder/HuffmanScanEncoder.cs
  4. 10
      tests/ImageSharp.Tests/Formats/Jpg/HuffmanScanEncoderTests.cs

60
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
/// <inheritdoc />
public override int GetHashCode() => (this[0] * 31) + this[1];
/// <summary>
/// Returns index of the last non-zero element in given matrix.
/// </summary>
/// <remarks>
/// Returns 0 for all-zero matrix by convention.
/// </remarks>
/// <returns>Index of the last non-zero element.</returns>
[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<int> zero8 = Vector256<int>.Zero;
ref Vector256<short> mcuStride = ref Unsafe.As<Block8x8, Vector256<short>>(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<Block8x8, short>(ref this);
while (index > 0 && Unsafe.Add(ref elemRef, index) == 0)
{
index--;
}
return index;
}
}
/// <summary>
/// Calculate the total sum of absolute differences of elements in 'a' and 'b'.
/// </summary>

55
src/ImageSharp/Formats/Jpeg/Components/Block8x8F.cs

@ -864,5 +864,60 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Components
return true;
}
}
/// <summary>
/// Returns index of the last non-zero element in this matrix.
/// </summary>
/// <returns>Index of the last non-zero element. Returns -1 if all elements are equal to zero.</returns>
[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<int> zero8 = Vector256<int>.Zero;
ref Vector256<float> mcuStride = ref Unsafe.As<Block8x8F, Vector256<float>>(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<Block8x8F, float>(ref this);
while (index >= 0 && (int)Unsafe.Add(ref elemRef, index) == 0)
{
index--;
}
return index;
}
}
}
}

61
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
}
/// <summary>
/// Returns index of the last non-zero element in given matrix.
/// </summary>
/// <remarks>
/// Returns 0 for all-zero matrix by convention.
/// </remarks>
/// <param name="mcu">Mcu block.</param>
/// <returns>Index of the last non-zero element.</returns>
[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<int> zero8 = Vector256<int>.Zero;
ref Vector256<float> 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<Block8x8F, float>(ref mcu);
while (index > 0 && (int)Unsafe.Add(ref elemRef, index) == 0)
{
index--;
}
return index;
}
}
[MethodImpl(InliningOptions.ShortMethod)]
private void WriteToStream()
{

10
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);
}

Loading…
Cancel
Save