Browse Source

Normalize AV1 transform output naming

pull/2633/head
James Jackson-South 2 months ago
parent
commit
9c57a2be87
  1. 49
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ByteInverseTransformOutputOperator.cs
  2. 40
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1HighBitDepthInverseTransformOutputOperator.cs
  3. 4
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs
  4. 85
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformOutputOperator.cs
  5. 4
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseWalshHadamardTransformer.cs
  6. 12
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1InverseTransformTests.cs

49
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ByteInverseTransformOutputOperator.cs

@ -1,49 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Reconstructs eight-bit AV1 samples from predicted values and inverse-transform residuals.
/// </summary>
internal readonly struct Av1ByteInverseTransformOutputOperator : IAv1InverseTransformOutputOperator<byte>
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte Add(byte prediction, int residual, int bitDepth)
{
_ = bitDepth;
return (byte)Math.Clamp(prediction + residual, byte.MinValue, byte.MaxValue);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref byte prediction, ref byte destination, Vector128<int> residual, int bitDepth)
{
uint packed = Unsafe.ReadUnaligned<uint>(ref prediction);
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
Vector128<int> predicted32 = Vector128.WidenLower(predicted16).AsInt32();
Vector128<int> reconstructed = Vector128.Clamp(predicted32 + residual, Vector128<int>.Zero, Vector128.Create((int)byte.MaxValue));
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.AsUInt32(), Vector128<uint>.Zero);
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero);
Unsafe.WriteUnaligned(ref destination, reconstructed8.AsUInt32().ToScalar());
_ = bitDepth;
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref byte prediction, ref byte destination, Vector256<int> residual, int bitDepth)
{
ulong packed = Unsafe.ReadUnaligned<ulong>(ref prediction);
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
Vector256<int> predicted32 = Vector256.Create(Vector128.WidenLower(predicted16), Vector128.WidenUpper(predicted16)).AsInt32();
Vector256<int> reconstructed = Vector256.Clamp(predicted32 + residual, Vector256<int>.Zero, Vector256.Create((int)byte.MaxValue));
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.GetLower().AsUInt32(), reconstructed.GetUpper().AsUInt32());
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero);
Unsafe.WriteUnaligned(ref destination, reconstructed8.AsUInt64().ToScalar());
_ = bitDepth;
}
}

40
src/ImageSharp/Formats/Heif/Av1/Transform/Av1HighBitDepthInverseTransformOutputOperator.cs

@ -1,40 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Common.Helpers;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Reconstructs high-bit-depth AV1 samples from predicted values and inverse-transform residuals.
/// </summary>
internal readonly struct Av1HighBitDepthInverseTransformOutputOperator : IAv1InverseTransformOutputOperator<short>
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short Add(short prediction, int residual, int bitDepth)
=> (short)Math.Clamp(prediction + residual, 0, (1 << bitDepth) - 1);
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref short prediction, ref short destination, Vector128<int> residual, int bitDepth)
{
ulong packed = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<short, byte>(ref prediction));
Vector128<int> predicted = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsInt16());
Vector128<int> reconstructed = Vector128.Clamp(predicted + residual, Vector128<int>.Zero, Vector128.Create((1 << bitDepth) - 1));
Vector128<short> narrowed = Vector128.Narrow(reconstructed, Vector128<int>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<short, byte>(ref destination), narrowed.AsUInt64().ToScalar());
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref short prediction, ref short destination, Vector256<int> residual, int bitDepth)
{
Vector256<int> predicted = Vector256_.Widen(Vector128.LoadUnsafe(ref prediction));
Vector256<int> reconstructed = Vector256.Clamp(predicted + residual, Vector256<int>.Zero, Vector256.Create((1 << bitDepth) - 1));
Vector128<short> narrowed = Vector128.Narrow(reconstructed.GetLower(), reconstructed.GetUpper());
narrowed.StoreUnsafe(ref destination);
}
}

4
src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs

@ -33,7 +33,7 @@ internal static class Av1Inverse2dTransformer
ref Av1Transform2dFlipConfiguration config,
Span<int> workspace,
int bitDepth)
=> Transform2dAdd<short, Av1HighBitDepthInverseTransformOutputOperator>(
=> Transform2dAdd<short, Av1InverseTransformOutputOperator<short>>(
input,
outputForRead,
strideForRead,
@ -61,7 +61,7 @@ internal static class Av1Inverse2dTransformer
int strideForWrite,
ref Av1Transform2dFlipConfiguration config,
Span<int> workspace)
=> Transform2dAdd<byte, Av1ByteInverseTransformOutputOperator>(
=> Transform2dAdd<byte, Av1InverseTransformOutputOperator<byte>>(
input,
outputForRead,
strideForRead,

85
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformOutputOperator.cs

@ -0,0 +1,85 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Common.Helpers;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Reconstructs AV1 samples from predicted values and inverse-transform residuals.
/// </summary>
/// <typeparam name="TSample">The decoded sample storage type.</typeparam>
internal readonly struct Av1InverseTransformOutputOperator<TSample> : IAv1InverseTransformOutputOperator<TSample>
where TSample : unmanaged
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TSample Add(TSample prediction, int residual, int bitDepth)
{
// TSample is fixed by the byte and short decoder entry points. The JIT removes this type test from each
// closed transform so storage selection does not introduce a branch in the reconstruction loop.
if (typeof(TSample) == typeof(byte))
{
byte value = (byte)Math.Clamp(Unsafe.As<TSample, byte>(ref prediction) + residual, byte.MinValue, byte.MaxValue);
return Unsafe.As<byte, TSample>(ref value);
}
short result = (short)Math.Clamp(Unsafe.As<TSample, short>(ref prediction) + residual, 0, (1 << bitDepth) - 1);
return Unsafe.As<short, TSample>(ref result);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref TSample prediction, ref TSample destination, Vector128<int> residual, int bitDepth)
{
if (typeof(TSample) == typeof(byte))
{
ref byte source = ref Unsafe.As<TSample, byte>(ref prediction);
uint packed = Unsafe.ReadUnaligned<uint>(ref source);
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
Vector128<int> predicted32 = Vector128.WidenLower(predicted16).AsInt32();
Vector128<int> reconstructed = Vector128.Clamp(predicted32 + residual, Vector128<int>.Zero, Vector128.Create((int)byte.MaxValue));
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.AsUInt32(), Vector128<uint>.Zero);
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<TSample, byte>(ref destination), reconstructed8.AsUInt32().ToScalar());
return;
}
ref short highBitDepthSource = ref Unsafe.As<TSample, short>(ref prediction);
ulong highBitDepthPacked = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<short, byte>(ref highBitDepthSource));
Vector128<int> highBitDepthPredicted = Vector128.WidenLower(Vector128.CreateScalarUnsafe(highBitDepthPacked).AsInt16());
Vector128<int> highBitDepthReconstructed =
Vector128.Clamp(highBitDepthPredicted + residual, Vector128<int>.Zero, Vector128.Create((1 << bitDepth) - 1));
Vector128<short> narrowed = Vector128.Narrow(highBitDepthReconstructed, Vector128<int>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<TSample, byte>(ref destination), narrowed.AsUInt64().ToScalar());
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref TSample prediction, ref TSample destination, Vector256<int> residual, int bitDepth)
{
if (typeof(TSample) == typeof(byte))
{
ref byte source = ref Unsafe.As<TSample, byte>(ref prediction);
ulong packed = Unsafe.ReadUnaligned<ulong>(ref source);
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
Vector256<int> predicted32 = Vector256.Create(Vector128.WidenLower(predicted16), Vector128.WidenUpper(predicted16)).AsInt32();
Vector256<int> reconstructed = Vector256.Clamp(predicted32 + residual, Vector256<int>.Zero, Vector256.Create((int)byte.MaxValue));
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.GetLower().AsUInt32(), reconstructed.GetUpper().AsUInt32());
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<TSample, byte>(ref destination), reconstructed8.AsUInt64().ToScalar());
return;
}
ref short highBitDepthSource = ref Unsafe.As<TSample, short>(ref prediction);
Vector256<int> highBitDepthPredicted = Vector256_.Widen(Vector128.LoadUnsafe(ref highBitDepthSource));
Vector256<int> highBitDepthReconstructed =
Vector256.Clamp(highBitDepthPredicted + residual, Vector256<int>.Zero, Vector256.Create((1 << bitDepth) - 1));
Vector128<short> narrowed = Vector128.Narrow(highBitDepthReconstructed.GetLower(), highBitDepthReconstructed.GetUpper());
narrowed.StoreUnsafe(ref Unsafe.As<TSample, short>(ref destination));
}
}

4
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseWalshHadamardTransformer.cs

@ -35,7 +35,7 @@ internal static class Av1InverseWalshHadamardTransformer
int writeStride,
int coefficientCount,
Span<int> workspace)
=> TransformAdd<byte, Av1ByteInverseTransformOutputOperator>(
=> TransformAdd<byte, Av1InverseTransformOutputOperator<byte>>(
coefficients,
readBuffer,
readStride,
@ -65,7 +65,7 @@ internal static class Av1InverseWalshHadamardTransformer
int coefficientCount,
Span<int> workspace,
int bitDepth)
=> TransformAdd<short, Av1HighBitDepthInverseTransformOutputOperator>(
=> TransformAdd<short, Av1InverseTransformOutputOperator<short>>(
coefficients,
readBuffer,
readStride,

12
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1InverseTransformTests.cs

@ -788,10 +788,10 @@ public class Av1InverseTransformTests
Array.Fill(scalar, byte.MaxValue);
Array.Fill(vector128, byte.MaxValue);
Av1Inverse2dTransformer.Transform2dScalar<byte, Av1ByteInverseTransformOutputOperator, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dScalar<byte, Av1InverseTransformOutputOperator<byte>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, scalar, writeStride, ref config, scalarWorkspace, bitDepth);
Av1Inverse2dTransformer.Transform2dVector128<byte, Av1ByteInverseTransformOutputOperator, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dVector128<byte, Av1InverseTransformOutputOperator<byte>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, vector128, writeStride, ref config, vector128Workspace, bitDepth);
Assert.Equal(scalar, vector128);
@ -802,7 +802,7 @@ public class Av1InverseTransformTests
int[] vector256Workspace = new int[workspaceLength];
Array.Fill(vector256, byte.MaxValue);
Av1Inverse2dTransformer.Transform2dVector256<byte, Av1ByteInverseTransformOutputOperator, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dVector256<byte, Av1InverseTransformOutputOperator<byte>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, vector256, writeStride, ref config, vector256Workspace, bitDepth);
Assert.Equal(scalar, vector256);
@ -849,10 +849,10 @@ public class Av1InverseTransformTests
Array.Fill(scalar, short.MinValue);
Array.Fill(vector128, short.MinValue);
Av1Inverse2dTransformer.Transform2dScalar<short, Av1HighBitDepthInverseTransformOutputOperator, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dScalar<short, Av1InverseTransformOutputOperator<short>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, scalar, writeStride, ref config, scalarWorkspace, bitDepth);
Av1Inverse2dTransformer.Transform2dVector128<short, Av1HighBitDepthInverseTransformOutputOperator, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dVector128<short, Av1InverseTransformOutputOperator<short>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, vector128, writeStride, ref config, vector128Workspace, bitDepth);
Assert.Equal(scalar, vector128);
@ -863,7 +863,7 @@ public class Av1InverseTransformTests
int[] vector256Workspace = new int[workspaceLength];
Array.Fill(vector256, short.MinValue);
Av1Inverse2dTransformer.Transform2dVector256<short, Av1HighBitDepthInverseTransformOutputOperator, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dVector256<short, Av1InverseTransformOutputOperator<short>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, vector256, writeStride, ref config, vector256Workspace, bitDepth);
Assert.Equal(scalar, vector256);

Loading…
Cancel
Save