mirror of https://github.com/SixLabors/ImageSharp
62 changed files with 6124 additions and 6278 deletions
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the sixteen-point asymmetric discrete sine forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the sixteen-point AV1 forward asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Adst16Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Adst16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the four-point asymmetric discrete sine forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the four-point AV1 forward asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Adst4Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Adst4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the eight-point asymmetric discrete sine forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 forward asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Adst8Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Adst8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the sixteen-point discrete cosine forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the sixteen-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Dct16Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the thirty-two-point discrete cosine forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the thirty-two-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Dct32Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct32(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the four-point discrete cosine forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the four-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Dct4Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the sixty-four-point discrete cosine forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the sixty-four-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Dct64Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct64(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the eight-point discrete cosine forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Dct8Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the sixteen-point identity forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the sixteen-point AV1 forward identity transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Identity16Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Identity16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the thirty-two-point identity forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the thirty-two-point AV1 forward identity transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Identity32Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Identity32(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the four-point identity forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the four-point AV1 forward identity transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Identity4Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Identity4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the eight-point identity forward transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 forward identity transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Identity8Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Identity8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
// 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>
|
|||
/// <remarks>
|
|||
/// Each SIMD lane represents one consecutive reconstructed sample. Packed byte or 16-bit predictions are widened to
|
|||
/// signed 32-bit lanes before residual addition, clipped to the coded sample range, and narrowed into exact-width
|
|||
/// stores. The closed <typeparamref name="TSample"/> specialization removes storage-type branches from hot loops.
|
|||
/// </remarks>
|
|||
/// <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)) |
|||
{ |
|||
// Read and write exactly four bytes. The unused upper lanes only participate in narrowing and never reach
|
|||
// memory, which keeps reconstruction valid at a tightly packed row boundary.
|
|||
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)) |
|||
{ |
|||
// Eight byte predictions widen through UInt16 into the eight Int32 residual lanes. The final 64-bit store
|
|||
// covers only those reconstructed samples and does not require destination padding.
|
|||
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)); |
|||
} |
|||
} |
|||
@ -1,574 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the sixteen-point asymmetric discrete sine inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the 16-point AV1 inverse asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Adst16Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative 16-point AV1 inverse asymmetric discrete sine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The sixteen frequency-domain coefficients.</param>
|
|||
/// <param name="output">The sixteen spatial-domain residual values.</param>
|
|||
/// <param name="step">The sixteen-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output[0] = input[15]; |
|||
output[1] = input[0]; |
|||
output[2] = input[13]; |
|||
output[3] = input[2]; |
|||
output[4] = input[11]; |
|||
output[5] = input[4]; |
|||
output[6] = input[9]; |
|||
output[7] = input[6]; |
|||
output[8] = input[7]; |
|||
output[9] = input[8]; |
|||
output[10] = input[5]; |
|||
output[11] = input[10]; |
|||
output[12] = input[3]; |
|||
output[13] = input[12]; |
|||
output[14] = input[1]; |
|||
output[15] = input[14]; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step[0] = Av1Transform1dMath.HalfButterfly(cospi[2], output[0], cospi[62], output[1], cosBit); |
|||
step[1] = Av1Transform1dMath.HalfButterfly(cospi[62], output[0], -cospi[2], output[1], cosBit); |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[10], output[2], cospi[54], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[54], output[2], -cospi[10], output[3], cosBit); |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[18], output[4], cospi[46], output[5], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[46], output[4], -cospi[18], output[5], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[26], output[6], cospi[38], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[38], output[6], -cospi[26], output[7], cosBit); |
|||
step[8] = Av1Transform1dMath.HalfButterfly(cospi[34], output[8], cospi[30], output[9], cosBit); |
|||
step[9] = Av1Transform1dMath.HalfButterfly(cospi[30], output[8], -cospi[34], output[9], cosBit); |
|||
step[10] = Av1Transform1dMath.HalfButterfly(cospi[42], output[10], cospi[22], output[11], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(cospi[22], output[10], -cospi[42], output[11], cosBit); |
|||
step[12] = Av1Transform1dMath.HalfButterfly(cospi[50], output[12], cospi[14], output[13], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[14], output[12], -cospi[50], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(cospi[58], output[14], cospi[6], output[15], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[6], output[14], -cospi[58], output[15], cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[8], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[9], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[10], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[11], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[12], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[5] + step[13], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[6] + step[14], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[7] + step[15], stageRange[stage]); |
|||
output[8] = Av1Transform1dMath.Clamp(step[0] - step[8], stageRange[stage]); |
|||
output[9] = Av1Transform1dMath.Clamp(step[1] - step[9], stageRange[stage]); |
|||
output[10] = Av1Transform1dMath.Clamp(step[2] - step[10], stageRange[stage]); |
|||
output[11] = Av1Transform1dMath.Clamp(step[3] - step[11], stageRange[stage]); |
|||
output[12] = Av1Transform1dMath.Clamp(step[4] - step[12], stageRange[stage]); |
|||
output[13] = Av1Transform1dMath.Clamp(step[5] - step[13], stageRange[stage]); |
|||
output[14] = Av1Transform1dMath.Clamp(step[6] - step[14], stageRange[stage]); |
|||
output[15] = Av1Transform1dMath.Clamp(step[7] - step[15], stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/16 rotations in the upper half.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = output[4]; |
|||
step[5] = output[5]; |
|||
step[6] = output[6]; |
|||
step[7] = output[7]; |
|||
step[8] = Av1Transform1dMath.HalfButterfly(cospi[8], output[8], cospi[56], output[9], cosBit); |
|||
step[9] = Av1Transform1dMath.HalfButterfly(cospi[56], output[8], -cospi[8], output[9], cosBit); |
|||
step[10] = Av1Transform1dMath.HalfButterfly(cospi[40], output[10], cospi[24], output[11], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(cospi[24], output[10], -cospi[40], output[11], cosBit); |
|||
step[12] = Av1Transform1dMath.HalfButterfly(-cospi[56], output[12], cospi[8], output[13], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[8], output[12], cospi[56], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(-cospi[24], output[14], cospi[40], output[15], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[40], output[14], cospi[24], output[15], cosBit); |
|||
|
|||
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[4], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[5], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[6], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[7], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[0] - step[4], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[1] - step[5], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[2] - step[6], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[3] - step[7], stageRange[stage]); |
|||
output[8] = Av1Transform1dMath.Clamp(step[8] + step[12], stageRange[stage]); |
|||
output[9] = Av1Transform1dMath.Clamp(step[9] + step[13], stageRange[stage]); |
|||
output[10] = Av1Transform1dMath.Clamp(step[10] + step[14], stageRange[stage]); |
|||
output[11] = Av1Transform1dMath.Clamp(step[11] + step[15], stageRange[stage]); |
|||
output[12] = Av1Transform1dMath.Clamp(step[8] - step[12], stageRange[stage]); |
|||
output[13] = Av1Transform1dMath.Clamp(step[9] - step[13], stageRange[stage]); |
|||
output[14] = Av1Transform1dMath.Clamp(step[10] - step[14], stageRange[stage]); |
|||
output[15] = Av1Transform1dMath.Clamp(step[11] - step[15], stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[16], output[4], cospi[48], output[5], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[48], output[4], -cospi[16], output[5], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[6], cospi[16], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[16], output[6], cospi[48], output[7], cosBit); |
|||
step[8] = output[8]; |
|||
step[9] = output[9]; |
|||
step[10] = output[10]; |
|||
step[11] = output[11]; |
|||
step[12] = Av1Transform1dMath.HalfButterfly(cospi[16], output[12], cospi[48], output[13], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[48], output[12], -cospi[16], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[14], cospi[16], output[15], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[16], output[14], cospi[48], output[15], cosBit); |
|||
|
|||
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[2], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[3], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[0] - step[2], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[1] - step[3], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[6], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[5] + step[7], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[4] - step[6], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[5] - step[7], stageRange[stage]); |
|||
output[8] = Av1Transform1dMath.Clamp(step[8] + step[10], stageRange[stage]); |
|||
output[9] = Av1Transform1dMath.Clamp(step[9] + step[11], stageRange[stage]); |
|||
output[10] = Av1Transform1dMath.Clamp(step[8] - step[10], stageRange[stage]); |
|||
output[11] = Av1Transform1dMath.Clamp(step[9] - step[11], stageRange[stage]); |
|||
output[12] = Av1Transform1dMath.Clamp(step[12] + step[14], stageRange[stage]); |
|||
output[13] = Av1Transform1dMath.Clamp(step[13] + step[15], stageRange[stage]); |
|||
output[14] = Av1Transform1dMath.Clamp(step[12] - step[14], stageRange[stage]); |
|||
output[15] = Av1Transform1dMath.Clamp(step[13] - step[15], stageRange[stage]); |
|||
|
|||
// Stage 8 reverses the pi/4 rotations for the middle pairs.
|
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], cospi[32], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], -cospi[32], output[3], cosBit); |
|||
step[4] = output[4]; |
|||
step[5] = output[5]; |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], cospi[32], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], -cospi[32], output[7], cosBit); |
|||
step[8] = output[8]; |
|||
step[9] = output[9]; |
|||
step[10] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], cospi[32], output[11], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], -cospi[32], output[11], cosBit); |
|||
step[12] = output[12]; |
|||
step[13] = output[13]; |
|||
step[14] = Av1Transform1dMath.HalfButterfly(cospi[32], output[14], cospi[32], output[15], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[32], output[14], -cospi[32], output[15], cosBit); |
|||
|
|||
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output[0] = step[0]; |
|||
output[1] = -step[8]; |
|||
output[2] = step[12]; |
|||
output[3] = -step[4]; |
|||
output[4] = step[6]; |
|||
output[5] = -step[14]; |
|||
output[6] = step[10]; |
|||
output[7] = -step[2]; |
|||
output[8] = step[3]; |
|||
output[9] = -step[11]; |
|||
output[10] = step[15]; |
|||
output[11] = -step[7]; |
|||
output[12] = step[5]; |
|||
output[13] = -step[13]; |
|||
output[14] = step[9]; |
|||
output[15] = -step[1]; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output.V0 = input.V15; |
|||
output.V1 = input.V0; |
|||
output.V2 = input.V13; |
|||
output.V3 = input.V2; |
|||
output.V4 = input.V11; |
|||
output.V5 = input.V4; |
|||
output.V6 = input.V9; |
|||
output.V7 = input.V6; |
|||
output.V8 = input.V7; |
|||
output.V9 = input.V8; |
|||
output.V10 = input.V5; |
|||
output.V11 = input.V10; |
|||
output.V12 = input.V3; |
|||
output.V13 = input.V12; |
|||
output.V14 = input.V1; |
|||
output.V15 = input.V14; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[2], output.V0, cospi[62], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[62], output.V0, -cospi[2], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[10], output.V2, cospi[54], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[54], output.V2, -cospi[10], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[18], output.V4, cospi[46], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[46], output.V4, -cospi[18], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[26], output.V6, cospi[38], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[38], output.V6, -cospi[26], output.V7, cosBit); |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[34], output.V8, cospi[30], output.V9, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[30], output.V8, -cospi[34], output.V9, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[42], output.V10, cospi[22], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[22], output.V10, -cospi[42], output.V11, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[50], output.V12, cospi[14], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[14], output.V12, -cospi[50], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[58], output.V14, cospi[6], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[6], output.V14, -cospi[58], output.V15, cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V8, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V9, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V10, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V11, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V12, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V13, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V14, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V15, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V0 - step.V8, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V1 - step.V9, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V2 - step.V10, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V3 - step.V11, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V4 - step.V12, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V5 - step.V13, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V6 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V7 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/16 rotations in the upper half.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = output.V6; |
|||
step.V7 = output.V7; |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V8, cospi[56], output.V9, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V8, -cospi[8], output.V9, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V10, cospi[24], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V10, -cospi[40], output.V11, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(-cospi[56], output.V12, cospi[8], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V12, cospi[56], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[24], output.V14, cospi[40], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V14, cospi[24], output.V15, cosBit); |
|||
|
|||
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V12, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V13, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V10 + step.V14, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V11 + step.V15, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V8 - step.V12, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V9 - step.V13, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V10 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V11 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = output.V10; |
|||
step.V11 = output.V11; |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V12, cospi[48], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V12, -cospi[16], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V14, cospi[16], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V14, cospi[48], output.V15, cosBit); |
|||
|
|||
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V10, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V11, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V8 - step.V10, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V9 - step.V11, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V14, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V13 + step.V15, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V12 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V13 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 8 reverses the pi/4 rotations for the middle pairs.
|
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit); |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, -cospi[32], output.V11, cosBit); |
|||
step.V12 = output.V12; |
|||
step.V13 = output.V13; |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, cospi[32], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, -cospi[32], output.V15, cosBit); |
|||
|
|||
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output.V0 = step.V0; |
|||
output.V1 = -step.V8; |
|||
output.V2 = step.V12; |
|||
output.V3 = -step.V4; |
|||
output.V4 = step.V6; |
|||
output.V5 = -step.V14; |
|||
output.V6 = step.V10; |
|||
output.V7 = -step.V2; |
|||
output.V8 = step.V3; |
|||
output.V9 = -step.V11; |
|||
output.V10 = step.V15; |
|||
output.V11 = -step.V7; |
|||
output.V12 = step.V5; |
|||
output.V13 = -step.V13; |
|||
output.V14 = step.V9; |
|||
output.V15 = -step.V1; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output.V0 = input.V15; |
|||
output.V1 = input.V0; |
|||
output.V2 = input.V13; |
|||
output.V3 = input.V2; |
|||
output.V4 = input.V11; |
|||
output.V5 = input.V4; |
|||
output.V6 = input.V9; |
|||
output.V7 = input.V6; |
|||
output.V8 = input.V7; |
|||
output.V9 = input.V8; |
|||
output.V10 = input.V5; |
|||
output.V11 = input.V10; |
|||
output.V12 = input.V3; |
|||
output.V13 = input.V12; |
|||
output.V14 = input.V1; |
|||
output.V15 = input.V14; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[2], output.V0, cospi[62], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[62], output.V0, -cospi[2], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[10], output.V2, cospi[54], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[54], output.V2, -cospi[10], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[18], output.V4, cospi[46], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[46], output.V4, -cospi[18], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[26], output.V6, cospi[38], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[38], output.V6, -cospi[26], output.V7, cosBit); |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[34], output.V8, cospi[30], output.V9, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[30], output.V8, -cospi[34], output.V9, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[42], output.V10, cospi[22], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[22], output.V10, -cospi[42], output.V11, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[50], output.V12, cospi[14], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[14], output.V12, -cospi[50], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[58], output.V14, cospi[6], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[6], output.V14, -cospi[58], output.V15, cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V8, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V9, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V10, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V11, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V12, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V13, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V14, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V15, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V0 - step.V8, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V1 - step.V9, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V2 - step.V10, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V3 - step.V11, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V4 - step.V12, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V5 - step.V13, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V6 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V7 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/16 rotations in the upper half.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = output.V6; |
|||
step.V7 = output.V7; |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V8, cospi[56], output.V9, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V8, -cospi[8], output.V9, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V10, cospi[24], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V10, -cospi[40], output.V11, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(-cospi[56], output.V12, cospi[8], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V12, cospi[56], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[24], output.V14, cospi[40], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V14, cospi[24], output.V15, cosBit); |
|||
|
|||
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V12, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V13, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V10 + step.V14, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V11 + step.V15, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V8 - step.V12, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V9 - step.V13, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V10 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V11 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = output.V10; |
|||
step.V11 = output.V11; |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V12, cospi[48], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V12, -cospi[16], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V14, cospi[16], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V14, cospi[48], output.V15, cosBit); |
|||
|
|||
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V10, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V11, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V8 - step.V10, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V9 - step.V11, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V14, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V13 + step.V15, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V12 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V13 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 8 reverses the pi/4 rotations for the middle pairs.
|
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit); |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, -cospi[32], output.V11, cosBit); |
|||
step.V12 = output.V12; |
|||
step.V13 = output.V13; |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, cospi[32], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, -cospi[32], output.V15, cosBit); |
|||
|
|||
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output.V0 = step.V0; |
|||
output.V1 = -step.V8; |
|||
output.V2 = step.V12; |
|||
output.V3 = -step.V4; |
|||
output.V4 = step.V6; |
|||
output.V5 = -step.V14; |
|||
output.V6 = step.V10; |
|||
output.V7 = -step.V2; |
|||
output.V8 = step.V3; |
|||
output.V9 = -step.V11; |
|||
output.V10 = step.V15; |
|||
output.V11 = -step.V7; |
|||
output.V12 = step.V5; |
|||
output.V13 = -step.V13; |
|||
output.V14 = step.V9; |
|||
output.V15 = -step.V1; |
|||
} |
|||
} |
|||
} |
|||
@ -1,174 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the four-point asymmetric discrete sine inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the four-point AV1 inverse asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Adst4Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative four-point AV1 inverse asymmetric discrete sine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The four frequency-domain coefficients.</param>
|
|||
/// <param name="output">The four spatial-domain residual values.</param>
|
|||
/// <param name="step">The stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
|
|||
// libaom widens the complete four-point factorization because the products retain their fixed-point scale
|
|||
// until the final shift. The stage buffer is therefore unnecessary for this transform size.
|
|||
long x0 = input[0]; |
|||
long x1 = input[1]; |
|||
long x2 = input[2]; |
|||
long x3 = input[3]; |
|||
|
|||
_ = step; |
|||
_ = stageRange; |
|||
|
|||
// Avoid the multiplications for the all-zero coefficient vector, matching libaom's scalar kernel.
|
|||
if ((x0 | x1 | x2 | x3) == 0) |
|||
{ |
|||
output[..4].Clear(); |
|||
return; |
|||
} |
|||
|
|||
// Stages 1 and 2 form the seven sine products and the one unscaled combination used by stage 3.
|
|||
long s0 = sinpi[1] * x0; |
|||
long s1 = sinpi[2] * x0; |
|||
long s2 = sinpi[3] * x1; |
|||
long s3 = sinpi[4] * x2; |
|||
long s4 = sinpi[1] * x2; |
|||
long s5 = sinpi[2] * x3; |
|||
long s6 = sinpi[4] * x3; |
|||
long s7 = (x0 - x2) + x3; |
|||
|
|||
// Stages 3 through 6 combine the products while preserving the fixed-point scale until the final rounding.
|
|||
s0 += s3; |
|||
s1 -= s4; |
|||
s3 = s2; |
|||
s2 = sinpi[3] * s7; |
|||
s0 += s5; |
|||
s1 -= s6; |
|||
x0 = s0 + s3; |
|||
x1 = s1 + s3; |
|||
x2 = s2; |
|||
x3 = (s0 + s1) - s3; |
|||
|
|||
output[0] = Av1Math.RoundShift(x0, cosBit); |
|||
output[1] = Av1Math.RoundShift(x1, cosBit); |
|||
output[2] = Av1Math.RoundShift(x2, cosBit); |
|||
output[3] = Av1Math.RoundShift(x3, cosBit); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
TransformCore(ref input, ref output, cosBit, stageRange[0] >= WidenedIntermediateBitCount); |
|||
_ = step; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
TransformCore(ref input, ref output, cosBit, stageRange[0] >= WidenedIntermediateBitCount); |
|||
_ = step; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the inverse four-point matrix to four independent axes.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for four transform axes.</param>
|
|||
/// <param name="output">The destination values for four transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
|
|||
/// <param name="widenedRound">Whether the terminal fixed-point rounding requires signed 64-bit lanes.</param>
|
|||
private static void TransformCore( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
int cosBit, |
|||
bool widenedRound) |
|||
{ |
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Vector128<int> x0 = input.V0; |
|||
Vector128<int> x1 = input.V1; |
|||
Vector128<int> x2 = input.V2; |
|||
Vector128<int> x3 = input.V3; |
|||
|
|||
// Pinned libaom retains the sine-table scale in Int32 products and sums, but performs the twelve-bit row
|
|||
// kernel's terminal scaling and rounding in Int64. This is the only stage whose rounding bias can overflow
|
|||
// a valid Int32 fixed-point sum.
|
|||
if (widenedRound) |
|||
{ |
|||
output.V0 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit); |
|||
output.V1 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit); |
|||
output.V2 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit); |
|||
output.V3 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit); |
|||
return; |
|||
} |
|||
|
|||
output.V0 = Av1Transform1dMath.MultiplyAdd4(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit); |
|||
output.V1 = Av1Transform1dMath.MultiplyAdd4(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit); |
|||
output.V2 = Av1Transform1dMath.MultiplyAdd4(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit); |
|||
output.V3 = Av1Transform1dMath.MultiplyAdd4(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the inverse four-point matrix to eight independent axes.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for eight transform axes.</param>
|
|||
/// <param name="output">The destination values for eight transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
|
|||
/// <param name="widenedRound">Whether the terminal fixed-point rounding requires signed 64-bit lanes.</param>
|
|||
private static void TransformCore( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
int cosBit, |
|||
bool widenedRound) |
|||
{ |
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Vector256<int> x0 = input.V0; |
|||
Vector256<int> x1 = input.V1; |
|||
Vector256<int> x2 = input.V2; |
|||
Vector256<int> x3 = input.V3; |
|||
|
|||
if (widenedRound) |
|||
{ |
|||
output.V0 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit); |
|||
output.V1 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit); |
|||
output.V2 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit); |
|||
output.V3 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit); |
|||
return; |
|||
} |
|||
|
|||
output.V0 = Av1Transform1dMath.MultiplyAdd4(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit); |
|||
output.V1 = Av1Transform1dMath.MultiplyAdd4(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit); |
|||
output.V2 = Av1Transform1dMath.MultiplyAdd4(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit); |
|||
output.V3 = Av1Transform1dMath.MultiplyAdd4(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit); |
|||
} |
|||
} |
|||
} |
|||
@ -1,295 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the eight-point asymmetric discrete sine inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 inverse asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Adst8Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative eight-point AV1 inverse asymmetric discrete sine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The eight frequency-domain coefficients.</param>
|
|||
/// <param name="output">The eight spatial-domain residual values.</param>
|
|||
/// <param name="step">The eight-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output[0] = input[7]; |
|||
output[1] = input[0]; |
|||
output[2] = input[5]; |
|||
output[3] = input[2]; |
|||
output[4] = input[3]; |
|||
output[5] = input[4]; |
|||
output[6] = input[1]; |
|||
output[7] = input[6]; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step[0] = Av1Transform1dMath.HalfButterfly(cospi[4], output[0], cospi[60], output[1], cosBit); |
|||
step[1] = Av1Transform1dMath.HalfButterfly(cospi[60], output[0], -cospi[4], output[1], cosBit); |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[20], output[2], cospi[44], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[44], output[2], -cospi[20], output[3], cosBit); |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[36], output[4], cospi[28], output[5], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[28], output[4], -cospi[36], output[5], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[52], output[6], cospi[12], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[12], output[6], -cospi[52], output[7], cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[4], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[5], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[6], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[7], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[0] - step[4], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[1] - step[5], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[2] - step[6], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[3] - step[7], stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[16], output[4], cospi[48], output[5], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[48], output[4], -cospi[16], output[5], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[6], cospi[16], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[16], output[6], cospi[48], output[7], cosBit); |
|||
|
|||
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[2], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[3], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[0] - step[2], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[1] - step[3], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[6], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[5] + step[7], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[4] - step[6], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[5] - step[7], stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/4 rotations for the middle pairs.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], cospi[32], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], -cospi[32], output[3], cosBit); |
|||
step[4] = output[4]; |
|||
step[5] = output[5]; |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], cospi[32], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], -cospi[32], output[7], cosBit); |
|||
|
|||
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output[0] = step[0]; |
|||
output[1] = -step[4]; |
|||
output[2] = step[6]; |
|||
output[3] = -step[2]; |
|||
output[4] = step[3]; |
|||
output[5] = -step[7]; |
|||
output[6] = step[5]; |
|||
output[7] = -step[1]; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output.V0 = input.V7; |
|||
output.V1 = input.V0; |
|||
output.V2 = input.V5; |
|||
output.V3 = input.V2; |
|||
output.V4 = input.V3; |
|||
output.V5 = input.V4; |
|||
output.V6 = input.V1; |
|||
output.V7 = input.V6; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V0, cospi[60], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V0, -cospi[4], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V2, cospi[44], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V2, -cospi[20], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V4, cospi[28], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V4, -cospi[36], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V6, cospi[12], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V6, -cospi[52], output.V7, cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit); |
|||
|
|||
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/4 rotations for the middle pairs.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit); |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit); |
|||
|
|||
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output.V0 = step.V0; |
|||
output.V1 = -step.V4; |
|||
output.V2 = step.V6; |
|||
output.V3 = -step.V2; |
|||
output.V4 = step.V3; |
|||
output.V5 = -step.V7; |
|||
output.V6 = step.V5; |
|||
output.V7 = -step.V1; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output.V0 = input.V7; |
|||
output.V1 = input.V0; |
|||
output.V2 = input.V5; |
|||
output.V3 = input.V2; |
|||
output.V4 = input.V3; |
|||
output.V5 = input.V4; |
|||
output.V6 = input.V1; |
|||
output.V7 = input.V6; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V0, cospi[60], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V0, -cospi[4], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V2, cospi[44], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V2, -cospi[20], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V4, cospi[28], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V4, -cospi[36], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V6, cospi[12], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V6, -cospi[52], output.V7, cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit); |
|||
|
|||
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/4 rotations for the middle pairs.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit); |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit); |
|||
|
|||
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output.V0 = step.V0; |
|||
output.V1 = -step.V4; |
|||
output.V2 = step.V6; |
|||
output.V3 = -step.V2; |
|||
output.V4 = step.V3; |
|||
output.V5 = -step.V7; |
|||
output.V6 = step.V5; |
|||
output.V7 = -step.V1; |
|||
} |
|||
} |
|||
} |
|||
@ -1,481 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the sixteen-point discrete cosine inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the 16-point AV1 inverse discrete cosine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Dct16Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative 16-point AV1 inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The sixteen frequency-domain coefficients.</param>
|
|||
/// <param name="output">The sixteen spatial-domain residual values.</param>
|
|||
/// <param name="step">The sixteen-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output[0] = input[0]; |
|||
output[1] = input[8]; |
|||
output[2] = input[4]; |
|||
output[3] = input[12]; |
|||
output[4] = input[2]; |
|||
output[5] = input[10]; |
|||
output[6] = input[6]; |
|||
output[7] = input[14]; |
|||
output[8] = input[1]; |
|||
output[9] = input[9]; |
|||
output[10] = input[5]; |
|||
output[11] = input[13]; |
|||
output[12] = input[3]; |
|||
output[13] = input[11]; |
|||
output[14] = input[7]; |
|||
output[15] = input[15]; |
|||
|
|||
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = output[4]; |
|||
step[5] = output[5]; |
|||
step[6] = output[6]; |
|||
step[7] = output[7]; |
|||
step[8] = Av1Transform1dMath.HalfButterfly(cospi[60], output[8], -cospi[4], output[15], cosBit); |
|||
step[9] = Av1Transform1dMath.HalfButterfly(cospi[28], output[9], -cospi[36], output[14], cosBit); |
|||
step[10] = Av1Transform1dMath.HalfButterfly(cospi[44], output[10], -cospi[20], output[13], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(cospi[12], output[11], -cospi[52], output[12], cosBit); |
|||
step[12] = Av1Transform1dMath.HalfButterfly(cospi[52], output[11], cospi[12], output[12], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[20], output[10], cospi[44], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(cospi[36], output[9], cospi[28], output[14], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[4], output[8], cospi[60], output[15], cosBit); |
|||
|
|||
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output[0] = step[0]; |
|||
output[1] = step[1]; |
|||
output[2] = step[2]; |
|||
output[3] = step[3]; |
|||
output[4] = Av1Transform1dMath.HalfButterfly(cospi[56], step[4], -cospi[8], step[7], cosBit); |
|||
output[5] = Av1Transform1dMath.HalfButterfly(cospi[24], step[5], -cospi[40], step[6], cosBit); |
|||
output[6] = Av1Transform1dMath.HalfButterfly(cospi[40], step[5], cospi[24], step[6], cosBit); |
|||
output[7] = Av1Transform1dMath.HalfButterfly(cospi[8], step[4], cospi[56], step[7], cosBit); |
|||
output[8] = Av1Transform1dMath.Clamp(step[8] + step[9], range); |
|||
output[9] = Av1Transform1dMath.Clamp(step[8] - step[9], range); |
|||
output[10] = Av1Transform1dMath.Clamp(step[11] - step[10], range); |
|||
output[11] = Av1Transform1dMath.Clamp(step[10] + step[11], range); |
|||
output[12] = Av1Transform1dMath.Clamp(step[12] + step[13], range); |
|||
output[13] = Av1Transform1dMath.Clamp(step[12] - step[13], range); |
|||
output[14] = Av1Transform1dMath.Clamp(step[15] - step[14], range); |
|||
output[15] = Av1Transform1dMath.Clamp(step[14] + step[15], range); |
|||
|
|||
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step[0] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], cospi[32], output[1], cosBit); |
|||
step[1] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], -cospi[32], output[1], cosBit); |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[48], output[2], -cospi[16], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[16], output[2], cospi[48], output[3], cosBit); |
|||
step[4] = Av1Transform1dMath.Clamp(output[4] + output[5], range); |
|||
step[5] = Av1Transform1dMath.Clamp(output[4] - output[5], range); |
|||
step[6] = Av1Transform1dMath.Clamp(output[7] - output[6], range); |
|||
step[7] = Av1Transform1dMath.Clamp(output[6] + output[7], range); |
|||
step[8] = output[8]; |
|||
step[9] = Av1Transform1dMath.HalfButterfly(-cospi[16], output[9], cospi[48], output[14], cosBit); |
|||
step[10] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[10], -cospi[16], output[13], cosBit); |
|||
step[11] = output[11]; |
|||
step[12] = output[12]; |
|||
step[13] = Av1Transform1dMath.HalfButterfly(-cospi[16], output[10], cospi[48], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(cospi[48], output[9], cospi[16], output[14], cosBit); |
|||
step[15] = output[15]; |
|||
|
|||
// Stage 5 widens the reconstructed groups through their next butterfly level.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[3], range); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[2], range); |
|||
output[2] = Av1Transform1dMath.Clamp(step[1] - step[2], range); |
|||
output[3] = Av1Transform1dMath.Clamp(step[0] - step[3], range); |
|||
output[4] = step[4]; |
|||
output[5] = Av1Transform1dMath.HalfButterfly(-cospi[32], step[5], cospi[32], step[6], cosBit); |
|||
output[6] = Av1Transform1dMath.HalfButterfly(cospi[32], step[5], cospi[32], step[6], cosBit); |
|||
output[7] = step[7]; |
|||
output[8] = Av1Transform1dMath.Clamp(step[8] + step[11], range); |
|||
output[9] = Av1Transform1dMath.Clamp(step[9] + step[10], range); |
|||
output[10] = Av1Transform1dMath.Clamp(step[9] - step[10], range); |
|||
output[11] = Av1Transform1dMath.Clamp(step[8] - step[11], range); |
|||
output[12] = Av1Transform1dMath.Clamp(step[15] - step[12], range); |
|||
output[13] = Av1Transform1dMath.Clamp(step[14] - step[13], range); |
|||
output[14] = Av1Transform1dMath.Clamp(step[13] + step[14], range); |
|||
output[15] = Av1Transform1dMath.Clamp(step[12] + step[15], range); |
|||
|
|||
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step[0] = Av1Transform1dMath.Clamp(output[0] + output[7], range); |
|||
step[1] = Av1Transform1dMath.Clamp(output[1] + output[6], range); |
|||
step[2] = Av1Transform1dMath.Clamp(output[2] + output[5], range); |
|||
step[3] = Av1Transform1dMath.Clamp(output[3] + output[4], range); |
|||
step[4] = Av1Transform1dMath.Clamp(output[3] - output[4], range); |
|||
step[5] = Av1Transform1dMath.Clamp(output[2] - output[5], range); |
|||
step[6] = Av1Transform1dMath.Clamp(output[1] - output[6], range); |
|||
step[7] = Av1Transform1dMath.Clamp(output[0] - output[7], range); |
|||
step[8] = output[8]; |
|||
step[9] = output[9]; |
|||
step[10] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[10], cospi[32], output[13], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[11], cospi[32], output[12], cosBit); |
|||
step[12] = Av1Transform1dMath.HalfButterfly(cospi[32], output[11], cospi[32], output[12], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], cospi[32], output[13], cosBit); |
|||
step[14] = output[14]; |
|||
step[15] = output[15]; |
|||
|
|||
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[15], range); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[14], range); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[13], range); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[12], range); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[11], range); |
|||
output[5] = Av1Transform1dMath.Clamp(step[5] + step[10], range); |
|||
output[6] = Av1Transform1dMath.Clamp(step[6] + step[9], range); |
|||
output[7] = Av1Transform1dMath.Clamp(step[7] + step[8], range); |
|||
output[8] = Av1Transform1dMath.Clamp(step[7] - step[8], range); |
|||
output[9] = Av1Transform1dMath.Clamp(step[6] - step[9], range); |
|||
output[10] = Av1Transform1dMath.Clamp(step[5] - step[10], range); |
|||
output[11] = Av1Transform1dMath.Clamp(step[4] - step[11], range); |
|||
output[12] = Av1Transform1dMath.Clamp(step[3] - step[12], range); |
|||
output[13] = Av1Transform1dMath.Clamp(step[2] - step[13], range); |
|||
output[14] = Av1Transform1dMath.Clamp(step[1] - step[14], range); |
|||
output[15] = Av1Transform1dMath.Clamp(step[0] - step[15], range); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output.V0 = input.V0; |
|||
output.V1 = input.V8; |
|||
output.V2 = input.V4; |
|||
output.V3 = input.V12; |
|||
output.V4 = input.V2; |
|||
output.V5 = input.V10; |
|||
output.V6 = input.V6; |
|||
output.V7 = input.V14; |
|||
output.V8 = input.V1; |
|||
output.V9 = input.V9; |
|||
output.V10 = input.V5; |
|||
output.V11 = input.V13; |
|||
output.V12 = input.V3; |
|||
output.V13 = input.V11; |
|||
output.V14 = input.V7; |
|||
output.V15 = input.V15; |
|||
|
|||
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = output.V6; |
|||
step.V7 = output.V7; |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V8, -cospi[4], output.V15, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V9, -cospi[36], output.V14, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V10, -cospi[20], output.V13, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V11, -cospi[52], output.V12, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V11, cospi[12], output.V12, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V10, cospi[44], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V9, cospi[28], output.V14, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V8, cospi[60], output.V15, cosBit); |
|||
|
|||
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output.V0 = step.V0; |
|||
output.V1 = step.V1; |
|||
output.V2 = step.V2; |
|||
output.V3 = step.V3; |
|||
output.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], step.V4, -cospi[8], step.V7, cosBit); |
|||
output.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], step.V5, -cospi[40], step.V6, cosBit); |
|||
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], step.V5, cospi[24], step.V6, cosBit); |
|||
output.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], step.V4, cospi[56], step.V7, cosBit); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V9, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V8 - step.V9, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V11 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V10 + step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V13, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V12 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V15 - step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V14 + step.V15, range); |
|||
|
|||
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.Clamp(output.V4 + output.V5, range); |
|||
step.V5 = Av1Transform1dMath.Clamp(output.V4 - output.V5, range); |
|||
step.V6 = Av1Transform1dMath.Clamp(output.V7 - output.V6, range); |
|||
step.V7 = Av1Transform1dMath.Clamp(output.V6 + output.V7, range); |
|||
step.V8 = output.V8; |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V9, cospi[48], output.V14, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V10, -cospi[16], output.V13, cosBit); |
|||
step.V11 = output.V11; |
|||
step.V12 = output.V12; |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V10, cospi[48], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V9, cospi[16], output.V14, cosBit); |
|||
step.V15 = output.V15; |
|||
|
|||
// Stage 5 widens the reconstructed groups through their next butterfly level.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range); |
|||
output.V4 = step.V4; |
|||
output.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], step.V5, cospi[32], step.V6, cosBit); |
|||
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V5, cospi[32], step.V6, cosBit); |
|||
output.V7 = step.V7; |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V11, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V10, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V9 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V8 - step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V15 - step.V12, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V14 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V13 + step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V12 + step.V15, range); |
|||
|
|||
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V7, range); |
|||
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V6, range); |
|||
step.V2 = Av1Transform1dMath.Clamp(output.V2 + output.V5, range); |
|||
step.V3 = Av1Transform1dMath.Clamp(output.V3 + output.V4, range); |
|||
step.V4 = Av1Transform1dMath.Clamp(output.V3 - output.V4, range); |
|||
step.V5 = Av1Transform1dMath.Clamp(output.V2 - output.V5, range); |
|||
step.V6 = Av1Transform1dMath.Clamp(output.V1 - output.V6, range); |
|||
step.V7 = Av1Transform1dMath.Clamp(output.V0 - output.V7, range); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V10, cospi[32], output.V13, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V11, cospi[32], output.V12, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V11, cospi[32], output.V12, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V13, cosBit); |
|||
step.V14 = output.V14; |
|||
step.V15 = output.V15; |
|||
|
|||
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V15, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V14, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V13, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V12, range); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V11, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V10, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V9, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V8, range); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V7 - step.V8, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V6 - step.V9, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V5 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V4 - step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V3 - step.V12, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V2 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V1 - step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V0 - step.V15, range); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output.V0 = input.V0; |
|||
output.V1 = input.V8; |
|||
output.V2 = input.V4; |
|||
output.V3 = input.V12; |
|||
output.V4 = input.V2; |
|||
output.V5 = input.V10; |
|||
output.V6 = input.V6; |
|||
output.V7 = input.V14; |
|||
output.V8 = input.V1; |
|||
output.V9 = input.V9; |
|||
output.V10 = input.V5; |
|||
output.V11 = input.V13; |
|||
output.V12 = input.V3; |
|||
output.V13 = input.V11; |
|||
output.V14 = input.V7; |
|||
output.V15 = input.V15; |
|||
|
|||
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = output.V6; |
|||
step.V7 = output.V7; |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V8, -cospi[4], output.V15, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V9, -cospi[36], output.V14, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V10, -cospi[20], output.V13, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V11, -cospi[52], output.V12, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V11, cospi[12], output.V12, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V10, cospi[44], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V9, cospi[28], output.V14, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V8, cospi[60], output.V15, cosBit); |
|||
|
|||
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output.V0 = step.V0; |
|||
output.V1 = step.V1; |
|||
output.V2 = step.V2; |
|||
output.V3 = step.V3; |
|||
output.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], step.V4, -cospi[8], step.V7, cosBit); |
|||
output.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], step.V5, -cospi[40], step.V6, cosBit); |
|||
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], step.V5, cospi[24], step.V6, cosBit); |
|||
output.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], step.V4, cospi[56], step.V7, cosBit); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V9, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V8 - step.V9, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V11 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V10 + step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V13, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V12 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V15 - step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V14 + step.V15, range); |
|||
|
|||
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.Clamp(output.V4 + output.V5, range); |
|||
step.V5 = Av1Transform1dMath.Clamp(output.V4 - output.V5, range); |
|||
step.V6 = Av1Transform1dMath.Clamp(output.V7 - output.V6, range); |
|||
step.V7 = Av1Transform1dMath.Clamp(output.V6 + output.V7, range); |
|||
step.V8 = output.V8; |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V9, cospi[48], output.V14, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V10, -cospi[16], output.V13, cosBit); |
|||
step.V11 = output.V11; |
|||
step.V12 = output.V12; |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V10, cospi[48], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V9, cospi[16], output.V14, cosBit); |
|||
step.V15 = output.V15; |
|||
|
|||
// Stage 5 widens the reconstructed groups through their next butterfly level.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range); |
|||
output.V4 = step.V4; |
|||
output.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], step.V5, cospi[32], step.V6, cosBit); |
|||
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V5, cospi[32], step.V6, cosBit); |
|||
output.V7 = step.V7; |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V11, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V10, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V9 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V8 - step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V15 - step.V12, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V14 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V13 + step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V12 + step.V15, range); |
|||
|
|||
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V7, range); |
|||
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V6, range); |
|||
step.V2 = Av1Transform1dMath.Clamp(output.V2 + output.V5, range); |
|||
step.V3 = Av1Transform1dMath.Clamp(output.V3 + output.V4, range); |
|||
step.V4 = Av1Transform1dMath.Clamp(output.V3 - output.V4, range); |
|||
step.V5 = Av1Transform1dMath.Clamp(output.V2 - output.V5, range); |
|||
step.V6 = Av1Transform1dMath.Clamp(output.V1 - output.V6, range); |
|||
step.V7 = Av1Transform1dMath.Clamp(output.V0 - output.V7, range); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V10, cospi[32], output.V13, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V11, cospi[32], output.V12, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V11, cospi[32], output.V12, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V13, cosBit); |
|||
step.V14 = output.V14; |
|||
step.V15 = output.V15; |
|||
|
|||
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V15, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V14, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V13, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V12, range); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V11, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V10, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V9, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V8, range); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V7 - step.V8, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V6 - step.V9, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V5 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V4 - step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V3 - step.V12, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V2 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V1 - step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V0 - step.V15, range); |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -1,118 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the four-point discrete cosine inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the four-point AV1 inverse discrete cosine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Dct4Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative four-point AV1 inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The four frequency-domain coefficients.</param>
|
|||
/// <param name="output">The four spatial-domain residual values.</param>
|
|||
/// <param name="step">The four-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
|
|||
output[0] = input[0]; |
|||
output[1] = input[2]; |
|||
output[2] = input[1]; |
|||
output[3] = input[3]; |
|||
|
|||
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
|
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
step[0] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], cospi[32], output[1], cosBit); |
|||
step[1] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], -cospi[32], output[1], cosBit); |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[48], output[2], -cospi[16], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[16], output[2], cospi[48], output[3], cosBit); |
|||
|
|||
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
|
|||
byte range = stageRange[3]; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[3], range); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[2], range); |
|||
output[2] = Av1Transform1dMath.Clamp(step[1] - step[2], range); |
|||
output[3] = Av1Transform1dMath.Clamp(step[0] - step[3], range); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
|
|||
output.V0 = input.V0; |
|||
output.V1 = input.V2; |
|||
output.V2 = input.V1; |
|||
output.V3 = input.V3; |
|||
|
|||
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
|
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit); |
|||
|
|||
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
|
|||
byte range = stageRange[3]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
|
|||
output.V0 = input.V0; |
|||
output.V1 = input.V2; |
|||
output.V2 = input.V1; |
|||
output.V3 = input.V3; |
|||
|
|||
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
|
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit); |
|||
|
|||
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
|
|||
byte range = stageRange[3]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range); |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -1,238 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the eight-point discrete cosine inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 inverse discrete cosine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Dct8Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative eight-point AV1 inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The eight frequency-domain coefficients.</param>
|
|||
/// <param name="output">The eight spatial-domain residual values.</param>
|
|||
/// <param name="step">The eight-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output[0] = input[0]; |
|||
output[1] = input[4]; |
|||
output[2] = input[2]; |
|||
output[3] = input[6]; |
|||
output[4] = input[1]; |
|||
output[5] = input[5]; |
|||
output[6] = input[3]; |
|||
output[7] = input[7]; |
|||
|
|||
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[56], output[4], -cospi[8], output[7], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[24], output[5], -cospi[40], output[6], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[40], output[5], cospi[24], output[6], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[8], output[4], cospi[56], output[7], cosBit); |
|||
|
|||
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output[0] = Av1Transform1dMath.HalfButterfly(cospi[32], step[0], cospi[32], step[1], cosBit); |
|||
output[1] = Av1Transform1dMath.HalfButterfly(cospi[32], step[0], -cospi[32], step[1], cosBit); |
|||
output[2] = Av1Transform1dMath.HalfButterfly(cospi[48], step[2], -cospi[16], step[3], cosBit); |
|||
output[3] = Av1Transform1dMath.HalfButterfly(cospi[16], step[2], cospi[48], step[3], cosBit); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[5], range); |
|||
output[5] = Av1Transform1dMath.Clamp(step[4] - step[5], range); |
|||
output[6] = Av1Transform1dMath.Clamp(step[7] - step[6], range); |
|||
output[7] = Av1Transform1dMath.Clamp(step[6] + step[7], range); |
|||
|
|||
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
|
|||
stage++; |
|||
step[0] = Av1Transform1dMath.Clamp(output[0] + output[3], range); |
|||
step[1] = Av1Transform1dMath.Clamp(output[1] + output[2], range); |
|||
step[2] = Av1Transform1dMath.Clamp(output[1] - output[2], range); |
|||
step[3] = Av1Transform1dMath.Clamp(output[0] - output[3], range); |
|||
step[4] = output[4]; |
|||
step[5] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[5], cospi[32], output[6], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[5], cospi[32], output[6], cosBit); |
|||
step[7] = output[7]; |
|||
|
|||
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[7], range); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[6], range); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[5], range); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[4], range); |
|||
output[4] = Av1Transform1dMath.Clamp(step[3] - step[4], range); |
|||
output[5] = Av1Transform1dMath.Clamp(step[2] - step[5], range); |
|||
output[6] = Av1Transform1dMath.Clamp(step[1] - step[6], range); |
|||
output[7] = Av1Transform1dMath.Clamp(step[0] - step[7], range); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output.V0 = input.V0; |
|||
output.V1 = input.V4; |
|||
output.V2 = input.V2; |
|||
output.V3 = input.V6; |
|||
output.V4 = input.V1; |
|||
output.V5 = input.V5; |
|||
output.V6 = input.V3; |
|||
output.V7 = input.V7; |
|||
|
|||
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V4, -cospi[8], output.V7, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V5, -cospi[40], output.V6, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V5, cospi[24], output.V6, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V4, cospi[56], output.V7, cosBit); |
|||
|
|||
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, cospi[32], step.V1, cosBit); |
|||
output.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, -cospi[32], step.V1, cosBit); |
|||
output.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], step.V2, -cospi[16], step.V3, cosBit); |
|||
output.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], step.V2, cospi[48], step.V3, cosBit); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V5, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V4 - step.V5, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V7 - step.V6, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V6 + step.V7, range); |
|||
|
|||
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V3, range); |
|||
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V2, range); |
|||
step.V2 = Av1Transform1dMath.Clamp(output.V1 - output.V2, range); |
|||
step.V3 = Av1Transform1dMath.Clamp(output.V0 - output.V3, range); |
|||
step.V4 = output.V4; |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V5, cospi[32], output.V6, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V5, cospi[32], output.V6, cosBit); |
|||
step.V7 = output.V7; |
|||
|
|||
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V7, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V6, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V5, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V4, range); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V3 - step.V4, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V2 - step.V5, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V1 - step.V6, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V0 - step.V7, range); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output.V0 = input.V0; |
|||
output.V1 = input.V4; |
|||
output.V2 = input.V2; |
|||
output.V3 = input.V6; |
|||
output.V4 = input.V1; |
|||
output.V5 = input.V5; |
|||
output.V6 = input.V3; |
|||
output.V7 = input.V7; |
|||
|
|||
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V4, -cospi[8], output.V7, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V5, -cospi[40], output.V6, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V5, cospi[24], output.V6, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V4, cospi[56], output.V7, cosBit); |
|||
|
|||
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, cospi[32], step.V1, cosBit); |
|||
output.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, -cospi[32], step.V1, cosBit); |
|||
output.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], step.V2, -cospi[16], step.V3, cosBit); |
|||
output.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], step.V2, cospi[48], step.V3, cosBit); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V5, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V4 - step.V5, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V7 - step.V6, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V6 + step.V7, range); |
|||
|
|||
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V3, range); |
|||
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V2, range); |
|||
step.V2 = Av1Transform1dMath.Clamp(output.V1 - output.V2, range); |
|||
step.V3 = Av1Transform1dMath.Clamp(output.V0 - output.V3, range); |
|||
step.V4 = output.V4; |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V5, cospi[32], output.V6, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V5, cospi[32], output.V6, cosBit); |
|||
step.V7 = output.V7; |
|||
|
|||
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V7, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V6, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V5, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V4, range); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V3 - step.V4, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V2 - step.V5, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V1 - step.V6, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V0 - step.V7, range); |
|||
} |
|||
} |
|||
} |
|||
@ -1,87 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the sixteen-point identity inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the sixteen-point AV1 inverse identity transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
|
|||
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
|
|||
/// </remarks>
|
|||
internal readonly struct Identity16Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative sixteen-point AV1 inverse identity transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The sixteen frequency-domain coefficients.</param>
|
|||
/// <param name="output">The sixteen scaled spatial-domain values.</param>
|
|||
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
|
|||
// The AV1 identity transform preserves coefficient order while applying the twice the square-root-of-two fixed-point scale required for 2-D normalization.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
output[i] = Av1Math.RoundShift((long)input[i] * (2 * Av1Transform1dMath.NewSqrt2), Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
// The doubled scale exceeds Int32 only for the 20-bit twelve-bit row range. Widen that exact product and
|
|||
// rounding sequence, matching libaom without changing the established lower-range SIMD path.
|
|||
if (stageRange[0] >= WidenedIntermediateBitCount) |
|||
{ |
|||
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
else |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
|
|||
_ = step; |
|||
_ = cosBit; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
if (stageRange[0] >= WidenedIntermediateBitCount) |
|||
{ |
|||
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
else |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
|
|||
_ = step; |
|||
_ = cosBit; |
|||
} |
|||
} |
|||
} |
|||
@ -1,71 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the thirty-two-point identity inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the thirty-two-point AV1 inverse identity transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
|
|||
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
|
|||
/// </remarks>
|
|||
internal readonly struct Identity32Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative thirty-two-point AV1 inverse identity transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The thirty-two frequency-domain coefficients.</param>
|
|||
/// <param name="output">The thirty-two scaled spatial-domain values.</param>
|
|||
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
|
|||
// The AV1 identity transform preserves coefficient order while applying the exact factor-of-four scale required for 2-D normalization.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
output[i] = input[i] * 4; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0); |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0); |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
} |
|||
} |
|||
} |
|||
@ -1,87 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the four-point identity inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the four-point AV1 inverse identity transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
|
|||
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
|
|||
/// </remarks>
|
|||
internal readonly struct Identity4Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative four-point AV1 inverse identity transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The four frequency-domain coefficients.</param>
|
|||
/// <param name="output">The four scaled spatial-domain values.</param>
|
|||
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
|
|||
// The AV1 identity transform preserves coefficient order while applying the square-root-of-two fixed-point scale required for 2-D normalization.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
output[i] = Av1Math.RoundShift((long)input[i] * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
// Only a twelve-bit row transform has the 20-bit input range that can overflow this fixed-point product.
|
|||
// Match libaom's high-bit-depth kernel there while retaining the compact Int32 path for narrower ranges.
|
|||
if (stageRange[0] >= WidenedIntermediateBitCount) |
|||
{ |
|||
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
else |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
|
|||
_ = step; |
|||
_ = cosBit; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
if (stageRange[0] >= WidenedIntermediateBitCount) |
|||
{ |
|||
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
else |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
|
|||
_ = step; |
|||
_ = cosBit; |
|||
} |
|||
} |
|||
} |
|||
@ -1,71 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Provides the eight-point identity inverse transform operator.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 inverse identity transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
|
|||
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
|
|||
/// </remarks>
|
|||
internal readonly struct Identity8Operator : IAv1InverseTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative eight-point AV1 inverse identity transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The eight frequency-domain coefficients.</param>
|
|||
/// <param name="output">The eight scaled spatial-domain values.</param>
|
|||
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
|
|||
// The AV1 identity transform preserves coefficient order while applying the exact factor-of-two scale required for 2-D normalization.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
output[i] = input[i] * 2; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0); |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0); |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
} |
|||
} |
|||
} |
|||
@ -1,100 +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; |
|||
|
|||
/// <content>
|
|||
/// Provides the sample-output operator shared by inverse transform traversals.
|
|||
/// </content>
|
|||
internal static partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Reconstructs AV1 samples from predicted values and inverse-transform residuals.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Each SIMD lane represents one consecutive reconstructed sample. Packed byte or 16-bit predictions are widened to
|
|||
/// signed 32-bit lanes before residual addition, clipped to the coded sample range, and narrowed into exact-width
|
|||
/// stores. The closed <typeparamref name="TSample"/> specialization removes storage-type branches from hot loops.
|
|||
/// </remarks>
|
|||
/// <typeparam name="TSample">The decoded sample storage type.</typeparam>
|
|||
internal readonly struct OutputOperator<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)) |
|||
{ |
|||
// Read and write exactly four bytes. The unused upper lanes only participate in narrowing and never reach
|
|||
// memory, which keeps reconstruction valid at a tightly packed row boundary.
|
|||
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)) |
|||
{ |
|||
// Eight byte predictions widen through UInt16 into the eight Int32 residual lanes. The final 64-bit store
|
|||
// covers only those reconstructed samples and does not require destination padding.
|
|||
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)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the sixteen-point AV1 forward asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Adst16Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Adst16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the four-point AV1 forward asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Adst4Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Adst4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 forward asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Adst8Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Adst8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the sixteen-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Dct16Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the thirty-two-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Dct32Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct32(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the four-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Dct4Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the sixty-four-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Dct64Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct64(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 forward discrete cosine transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Dct8Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Dct8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the sixteen-point AV1 forward identity transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Identity16Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Identity16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the thirty-two-point AV1 forward identity transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Identity32Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Identity32(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the four-point AV1 forward identity transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Identity4Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Identity4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 forward identity transform operator.
|
|||
/// </summary>
|
|||
internal readonly struct Av1Identity8Forward1dOperator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Av1ForwardTransformOperations.Identity8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit); |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <summary>
|
|||
/// Defines one AV1 forward transform which can be specialized for the selected sample and SIMD lane type.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// A concrete operator identifies the transform stage network. The two-dimensional driver selects the sample type
|
|||
/// and vector width once per block, allowing the JIT to specialize the complete network without interface dispatch
|
|||
/// inside the transform stages.
|
|||
/// </remarks>
|
|||
internal interface IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Transforms the independent axes stored in each value lane.
|
|||
/// </summary>
|
|||
/// <typeparam name="TValue">The scalar or SIMD value containing the independent transform axes.</typeparam>
|
|||
/// <param name="values">The first value in the strided transform block.</param>
|
|||
/// <param name="inputStride">The byte distance between consecutive input positions.</param>
|
|||
/// <param name="outputStride">The byte distance between consecutive output positions.</param>
|
|||
/// <param name="buffer0">The first fixed transform-stage buffer.</param>
|
|||
/// <param name="buffer1">The second fixed transform-stage buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
public static abstract void Transform<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct; |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <summary>
|
|||
/// Defines how inverse-transform residuals are added to a decoded sample representation.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Residual lanes correspond to consecutive reconstructed samples. Implementations must widen packed predictions,
|
|||
/// add and clip in signed 32-bit lanes, then store exactly four or eight results so callers do not require writable
|
|||
/// padding beyond the transform block. The closed sample type allows byte and high-bit-depth storage to specialize.
|
|||
/// </remarks>
|
|||
/// <typeparam name="TSample">The decoded sample storage type.</typeparam>
|
|||
internal interface IAv1InverseTransformOutputOperator<TSample> |
|||
where TSample : unmanaged |
|||
{ |
|||
/// <summary>
|
|||
/// Adds one residual to a predicted sample and clips the result to the coded bit depth.
|
|||
/// </summary>
|
|||
/// <param name="prediction">The predicted sample.</param>
|
|||
/// <param name="residual">The inverse-transform residual.</param>
|
|||
/// <param name="bitDepth">The coded sample bit depth.</param>
|
|||
/// <returns>The reconstructed sample.</returns>
|
|||
public static abstract TSample Add(TSample prediction, int residual, int bitDepth); |
|||
|
|||
/// <summary>
|
|||
/// Adds four residuals to four predicted samples and stores the clipped results.
|
|||
/// </summary>
|
|||
/// <param name="prediction">The first predicted sample.</param>
|
|||
/// <param name="destination">The first destination sample.</param>
|
|||
/// <param name="residual">The four inverse-transform residuals.</param>
|
|||
/// <param name="bitDepth">The coded sample bit depth.</param>
|
|||
public static abstract void Add(ref TSample prediction, ref TSample destination, Vector128<int> residual, int bitDepth); |
|||
|
|||
/// <summary>
|
|||
/// Adds eight residuals to eight predicted samples and stores the clipped results.
|
|||
/// </summary>
|
|||
/// <param name="prediction">The first predicted sample.</param>
|
|||
/// <param name="destination">The first destination sample.</param>
|
|||
/// <param name="residual">The eight inverse-transform residuals.</param>
|
|||
/// <param name="bitDepth">The coded sample bit depth.</param>
|
|||
public static abstract void Add(ref TSample prediction, ref TSample destination, Vector256<int> residual, int bitDepth); |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <summary>
|
|||
/// Defines the scalar and SIMD arithmetic for one AV1 one-dimensional transform.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Each overload performs the same staged fixed-point transform. In the SIMD overloads, each vector field identifies
|
|||
/// one coefficient position and each lane identifies an independent row or column. Butterfly arithmetic is therefore
|
|||
/// lane-local: vectorization changes only how many axes advance together, not coefficient order, rounding, or stage
|
|||
/// clamping. The two-dimensional traversal selects the concrete operator and lane width once per block, allowing the
|
|||
/// JIT to specialize every static interface call outside the stage network.
|
|||
/// </remarks>
|
|||
internal interface IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Transforms one axis when hardware vectorization is unavailable.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the transform axis.</param>
|
|||
/// <param name="output">The destination values for the transform axis.</param>
|
|||
/// <param name="step">The fixed stage storage for the transform axis.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static abstract void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange); |
|||
|
|||
/// <summary>
|
|||
/// Transforms four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for four transform axes.</param>
|
|||
/// <param name="output">The destination values for four transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for four transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static abstract void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange); |
|||
|
|||
/// <summary>
|
|||
/// Transforms eight independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for eight transform axes.</param>
|
|||
/// <param name="output">The destination values for eight transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for eight transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static abstract void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange); |
|||
} |
|||
@ -0,0 +1,568 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the 16-point AV1 inverse asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Adst16Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative 16-point AV1 inverse asymmetric discrete sine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The sixteen frequency-domain coefficients.</param>
|
|||
/// <param name="output">The sixteen spatial-domain residual values.</param>
|
|||
/// <param name="step">The sixteen-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output[0] = input[15]; |
|||
output[1] = input[0]; |
|||
output[2] = input[13]; |
|||
output[3] = input[2]; |
|||
output[4] = input[11]; |
|||
output[5] = input[4]; |
|||
output[6] = input[9]; |
|||
output[7] = input[6]; |
|||
output[8] = input[7]; |
|||
output[9] = input[8]; |
|||
output[10] = input[5]; |
|||
output[11] = input[10]; |
|||
output[12] = input[3]; |
|||
output[13] = input[12]; |
|||
output[14] = input[1]; |
|||
output[15] = input[14]; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step[0] = Av1Transform1dMath.HalfButterfly(cospi[2], output[0], cospi[62], output[1], cosBit); |
|||
step[1] = Av1Transform1dMath.HalfButterfly(cospi[62], output[0], -cospi[2], output[1], cosBit); |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[10], output[2], cospi[54], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[54], output[2], -cospi[10], output[3], cosBit); |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[18], output[4], cospi[46], output[5], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[46], output[4], -cospi[18], output[5], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[26], output[6], cospi[38], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[38], output[6], -cospi[26], output[7], cosBit); |
|||
step[8] = Av1Transform1dMath.HalfButterfly(cospi[34], output[8], cospi[30], output[9], cosBit); |
|||
step[9] = Av1Transform1dMath.HalfButterfly(cospi[30], output[8], -cospi[34], output[9], cosBit); |
|||
step[10] = Av1Transform1dMath.HalfButterfly(cospi[42], output[10], cospi[22], output[11], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(cospi[22], output[10], -cospi[42], output[11], cosBit); |
|||
step[12] = Av1Transform1dMath.HalfButterfly(cospi[50], output[12], cospi[14], output[13], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[14], output[12], -cospi[50], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(cospi[58], output[14], cospi[6], output[15], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[6], output[14], -cospi[58], output[15], cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[8], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[9], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[10], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[11], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[12], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[5] + step[13], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[6] + step[14], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[7] + step[15], stageRange[stage]); |
|||
output[8] = Av1Transform1dMath.Clamp(step[0] - step[8], stageRange[stage]); |
|||
output[9] = Av1Transform1dMath.Clamp(step[1] - step[9], stageRange[stage]); |
|||
output[10] = Av1Transform1dMath.Clamp(step[2] - step[10], stageRange[stage]); |
|||
output[11] = Av1Transform1dMath.Clamp(step[3] - step[11], stageRange[stage]); |
|||
output[12] = Av1Transform1dMath.Clamp(step[4] - step[12], stageRange[stage]); |
|||
output[13] = Av1Transform1dMath.Clamp(step[5] - step[13], stageRange[stage]); |
|||
output[14] = Av1Transform1dMath.Clamp(step[6] - step[14], stageRange[stage]); |
|||
output[15] = Av1Transform1dMath.Clamp(step[7] - step[15], stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/16 rotations in the upper half.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = output[4]; |
|||
step[5] = output[5]; |
|||
step[6] = output[6]; |
|||
step[7] = output[7]; |
|||
step[8] = Av1Transform1dMath.HalfButterfly(cospi[8], output[8], cospi[56], output[9], cosBit); |
|||
step[9] = Av1Transform1dMath.HalfButterfly(cospi[56], output[8], -cospi[8], output[9], cosBit); |
|||
step[10] = Av1Transform1dMath.HalfButterfly(cospi[40], output[10], cospi[24], output[11], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(cospi[24], output[10], -cospi[40], output[11], cosBit); |
|||
step[12] = Av1Transform1dMath.HalfButterfly(-cospi[56], output[12], cospi[8], output[13], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[8], output[12], cospi[56], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(-cospi[24], output[14], cospi[40], output[15], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[40], output[14], cospi[24], output[15], cosBit); |
|||
|
|||
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[4], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[5], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[6], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[7], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[0] - step[4], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[1] - step[5], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[2] - step[6], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[3] - step[7], stageRange[stage]); |
|||
output[8] = Av1Transform1dMath.Clamp(step[8] + step[12], stageRange[stage]); |
|||
output[9] = Av1Transform1dMath.Clamp(step[9] + step[13], stageRange[stage]); |
|||
output[10] = Av1Transform1dMath.Clamp(step[10] + step[14], stageRange[stage]); |
|||
output[11] = Av1Transform1dMath.Clamp(step[11] + step[15], stageRange[stage]); |
|||
output[12] = Av1Transform1dMath.Clamp(step[8] - step[12], stageRange[stage]); |
|||
output[13] = Av1Transform1dMath.Clamp(step[9] - step[13], stageRange[stage]); |
|||
output[14] = Av1Transform1dMath.Clamp(step[10] - step[14], stageRange[stage]); |
|||
output[15] = Av1Transform1dMath.Clamp(step[11] - step[15], stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[16], output[4], cospi[48], output[5], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[48], output[4], -cospi[16], output[5], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[6], cospi[16], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[16], output[6], cospi[48], output[7], cosBit); |
|||
step[8] = output[8]; |
|||
step[9] = output[9]; |
|||
step[10] = output[10]; |
|||
step[11] = output[11]; |
|||
step[12] = Av1Transform1dMath.HalfButterfly(cospi[16], output[12], cospi[48], output[13], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[48], output[12], -cospi[16], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[14], cospi[16], output[15], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[16], output[14], cospi[48], output[15], cosBit); |
|||
|
|||
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[2], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[3], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[0] - step[2], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[1] - step[3], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[6], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[5] + step[7], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[4] - step[6], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[5] - step[7], stageRange[stage]); |
|||
output[8] = Av1Transform1dMath.Clamp(step[8] + step[10], stageRange[stage]); |
|||
output[9] = Av1Transform1dMath.Clamp(step[9] + step[11], stageRange[stage]); |
|||
output[10] = Av1Transform1dMath.Clamp(step[8] - step[10], stageRange[stage]); |
|||
output[11] = Av1Transform1dMath.Clamp(step[9] - step[11], stageRange[stage]); |
|||
output[12] = Av1Transform1dMath.Clamp(step[12] + step[14], stageRange[stage]); |
|||
output[13] = Av1Transform1dMath.Clamp(step[13] + step[15], stageRange[stage]); |
|||
output[14] = Av1Transform1dMath.Clamp(step[12] - step[14], stageRange[stage]); |
|||
output[15] = Av1Transform1dMath.Clamp(step[13] - step[15], stageRange[stage]); |
|||
|
|||
// Stage 8 reverses the pi/4 rotations for the middle pairs.
|
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], cospi[32], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], -cospi[32], output[3], cosBit); |
|||
step[4] = output[4]; |
|||
step[5] = output[5]; |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], cospi[32], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], -cospi[32], output[7], cosBit); |
|||
step[8] = output[8]; |
|||
step[9] = output[9]; |
|||
step[10] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], cospi[32], output[11], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], -cospi[32], output[11], cosBit); |
|||
step[12] = output[12]; |
|||
step[13] = output[13]; |
|||
step[14] = Av1Transform1dMath.HalfButterfly(cospi[32], output[14], cospi[32], output[15], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[32], output[14], -cospi[32], output[15], cosBit); |
|||
|
|||
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output[0] = step[0]; |
|||
output[1] = -step[8]; |
|||
output[2] = step[12]; |
|||
output[3] = -step[4]; |
|||
output[4] = step[6]; |
|||
output[5] = -step[14]; |
|||
output[6] = step[10]; |
|||
output[7] = -step[2]; |
|||
output[8] = step[3]; |
|||
output[9] = -step[11]; |
|||
output[10] = step[15]; |
|||
output[11] = -step[7]; |
|||
output[12] = step[5]; |
|||
output[13] = -step[13]; |
|||
output[14] = step[9]; |
|||
output[15] = -step[1]; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output.V0 = input.V15; |
|||
output.V1 = input.V0; |
|||
output.V2 = input.V13; |
|||
output.V3 = input.V2; |
|||
output.V4 = input.V11; |
|||
output.V5 = input.V4; |
|||
output.V6 = input.V9; |
|||
output.V7 = input.V6; |
|||
output.V8 = input.V7; |
|||
output.V9 = input.V8; |
|||
output.V10 = input.V5; |
|||
output.V11 = input.V10; |
|||
output.V12 = input.V3; |
|||
output.V13 = input.V12; |
|||
output.V14 = input.V1; |
|||
output.V15 = input.V14; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[2], output.V0, cospi[62], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[62], output.V0, -cospi[2], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[10], output.V2, cospi[54], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[54], output.V2, -cospi[10], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[18], output.V4, cospi[46], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[46], output.V4, -cospi[18], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[26], output.V6, cospi[38], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[38], output.V6, -cospi[26], output.V7, cosBit); |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[34], output.V8, cospi[30], output.V9, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[30], output.V8, -cospi[34], output.V9, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[42], output.V10, cospi[22], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[22], output.V10, -cospi[42], output.V11, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[50], output.V12, cospi[14], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[14], output.V12, -cospi[50], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[58], output.V14, cospi[6], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[6], output.V14, -cospi[58], output.V15, cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V8, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V9, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V10, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V11, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V12, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V13, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V14, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V15, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V0 - step.V8, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V1 - step.V9, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V2 - step.V10, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V3 - step.V11, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V4 - step.V12, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V5 - step.V13, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V6 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V7 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/16 rotations in the upper half.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = output.V6; |
|||
step.V7 = output.V7; |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V8, cospi[56], output.V9, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V8, -cospi[8], output.V9, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V10, cospi[24], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V10, -cospi[40], output.V11, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(-cospi[56], output.V12, cospi[8], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V12, cospi[56], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[24], output.V14, cospi[40], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V14, cospi[24], output.V15, cosBit); |
|||
|
|||
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V12, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V13, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V10 + step.V14, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V11 + step.V15, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V8 - step.V12, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V9 - step.V13, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V10 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V11 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = output.V10; |
|||
step.V11 = output.V11; |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V12, cospi[48], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V12, -cospi[16], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V14, cospi[16], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V14, cospi[48], output.V15, cosBit); |
|||
|
|||
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V10, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V11, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V8 - step.V10, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V9 - step.V11, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V14, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V13 + step.V15, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V12 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V13 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 8 reverses the pi/4 rotations for the middle pairs.
|
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit); |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, -cospi[32], output.V11, cosBit); |
|||
step.V12 = output.V12; |
|||
step.V13 = output.V13; |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, cospi[32], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, -cospi[32], output.V15, cosBit); |
|||
|
|||
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output.V0 = step.V0; |
|||
output.V1 = -step.V8; |
|||
output.V2 = step.V12; |
|||
output.V3 = -step.V4; |
|||
output.V4 = step.V6; |
|||
output.V5 = -step.V14; |
|||
output.V6 = step.V10; |
|||
output.V7 = -step.V2; |
|||
output.V8 = step.V3; |
|||
output.V9 = -step.V11; |
|||
output.V10 = step.V15; |
|||
output.V11 = -step.V7; |
|||
output.V12 = step.V5; |
|||
output.V13 = -step.V13; |
|||
output.V14 = step.V9; |
|||
output.V15 = -step.V1; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output.V0 = input.V15; |
|||
output.V1 = input.V0; |
|||
output.V2 = input.V13; |
|||
output.V3 = input.V2; |
|||
output.V4 = input.V11; |
|||
output.V5 = input.V4; |
|||
output.V6 = input.V9; |
|||
output.V7 = input.V6; |
|||
output.V8 = input.V7; |
|||
output.V9 = input.V8; |
|||
output.V10 = input.V5; |
|||
output.V11 = input.V10; |
|||
output.V12 = input.V3; |
|||
output.V13 = input.V12; |
|||
output.V14 = input.V1; |
|||
output.V15 = input.V14; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[2], output.V0, cospi[62], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[62], output.V0, -cospi[2], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[10], output.V2, cospi[54], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[54], output.V2, -cospi[10], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[18], output.V4, cospi[46], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[46], output.V4, -cospi[18], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[26], output.V6, cospi[38], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[38], output.V6, -cospi[26], output.V7, cosBit); |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[34], output.V8, cospi[30], output.V9, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[30], output.V8, -cospi[34], output.V9, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[42], output.V10, cospi[22], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[22], output.V10, -cospi[42], output.V11, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[50], output.V12, cospi[14], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[14], output.V12, -cospi[50], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[58], output.V14, cospi[6], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[6], output.V14, -cospi[58], output.V15, cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V8, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V9, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V10, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V11, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V12, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V13, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V14, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V15, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V0 - step.V8, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V1 - step.V9, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V2 - step.V10, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V3 - step.V11, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V4 - step.V12, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V5 - step.V13, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V6 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V7 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/16 rotations in the upper half.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = output.V6; |
|||
step.V7 = output.V7; |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V8, cospi[56], output.V9, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V8, -cospi[8], output.V9, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V10, cospi[24], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V10, -cospi[40], output.V11, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(-cospi[56], output.V12, cospi[8], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V12, cospi[56], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[24], output.V14, cospi[40], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V14, cospi[24], output.V15, cosBit); |
|||
|
|||
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V12, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V13, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V10 + step.V14, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V11 + step.V15, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V8 - step.V12, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V9 - step.V13, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V10 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V11 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = output.V10; |
|||
step.V11 = output.V11; |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V12, cospi[48], output.V13, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V12, -cospi[16], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V14, cospi[16], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V14, cospi[48], output.V15, cosBit); |
|||
|
|||
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V10, stageRange[stage]); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V11, stageRange[stage]); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V8 - step.V10, stageRange[stage]); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V9 - step.V11, stageRange[stage]); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V14, stageRange[stage]); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V13 + step.V15, stageRange[stage]); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V12 - step.V14, stageRange[stage]); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V13 - step.V15, stageRange[stage]); |
|||
|
|||
// Stage 8 reverses the pi/4 rotations for the middle pairs.
|
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit); |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V11, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, -cospi[32], output.V11, cosBit); |
|||
step.V12 = output.V12; |
|||
step.V13 = output.V13; |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, cospi[32], output.V15, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, -cospi[32], output.V15, cosBit); |
|||
|
|||
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output.V0 = step.V0; |
|||
output.V1 = -step.V8; |
|||
output.V2 = step.V12; |
|||
output.V3 = -step.V4; |
|||
output.V4 = step.V6; |
|||
output.V5 = -step.V14; |
|||
output.V6 = step.V10; |
|||
output.V7 = -step.V2; |
|||
output.V8 = step.V3; |
|||
output.V9 = -step.V11; |
|||
output.V10 = step.V15; |
|||
output.V11 = -step.V7; |
|||
output.V12 = step.V5; |
|||
output.V13 = -step.V13; |
|||
output.V14 = step.V9; |
|||
output.V15 = -step.V1; |
|||
} |
|||
} |
|||
@ -0,0 +1,168 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the four-point AV1 inverse asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Adst4Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative four-point AV1 inverse asymmetric discrete sine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The four frequency-domain coefficients.</param>
|
|||
/// <param name="output">The four spatial-domain residual values.</param>
|
|||
/// <param name="step">The stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
|
|||
// libaom widens the complete four-point factorization because the products retain their fixed-point scale
|
|||
// until the final shift. The stage buffer is therefore unnecessary for this transform size.
|
|||
long x0 = input[0]; |
|||
long x1 = input[1]; |
|||
long x2 = input[2]; |
|||
long x3 = input[3]; |
|||
|
|||
_ = step; |
|||
_ = stageRange; |
|||
|
|||
// Avoid the multiplications for the all-zero coefficient vector, matching libaom's scalar kernel.
|
|||
if ((x0 | x1 | x2 | x3) == 0) |
|||
{ |
|||
output[..4].Clear(); |
|||
return; |
|||
} |
|||
|
|||
// Stages 1 and 2 form the seven sine products and the one unscaled combination used by stage 3.
|
|||
long s0 = sinpi[1] * x0; |
|||
long s1 = sinpi[2] * x0; |
|||
long s2 = sinpi[3] * x1; |
|||
long s3 = sinpi[4] * x2; |
|||
long s4 = sinpi[1] * x2; |
|||
long s5 = sinpi[2] * x3; |
|||
long s6 = sinpi[4] * x3; |
|||
long s7 = (x0 - x2) + x3; |
|||
|
|||
// Stages 3 through 6 combine the products while preserving the fixed-point scale until the final rounding.
|
|||
s0 += s3; |
|||
s1 -= s4; |
|||
s3 = s2; |
|||
s2 = sinpi[3] * s7; |
|||
s0 += s5; |
|||
s1 -= s6; |
|||
x0 = s0 + s3; |
|||
x1 = s1 + s3; |
|||
x2 = s2; |
|||
x3 = (s0 + s1) - s3; |
|||
|
|||
output[0] = Av1Math.RoundShift(x0, cosBit); |
|||
output[1] = Av1Math.RoundShift(x1, cosBit); |
|||
output[2] = Av1Math.RoundShift(x2, cosBit); |
|||
output[3] = Av1Math.RoundShift(x3, cosBit); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
TransformCore(ref input, ref output, cosBit, stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount); |
|||
_ = step; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
TransformCore(ref input, ref output, cosBit, stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount); |
|||
_ = step; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the inverse four-point matrix to four independent axes.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for four transform axes.</param>
|
|||
/// <param name="output">The destination values for four transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
|
|||
/// <param name="widenedRound">Whether the terminal fixed-point rounding requires signed 64-bit lanes.</param>
|
|||
private static void TransformCore( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
int cosBit, |
|||
bool widenedRound) |
|||
{ |
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Vector128<int> x0 = input.V0; |
|||
Vector128<int> x1 = input.V1; |
|||
Vector128<int> x2 = input.V2; |
|||
Vector128<int> x3 = input.V3; |
|||
|
|||
// Pinned libaom retains the sine-table scale in Int32 products and sums, but performs the twelve-bit row
|
|||
// kernel's terminal scaling and rounding in Int64. This is the only stage whose rounding bias can overflow
|
|||
// a valid Int32 fixed-point sum.
|
|||
if (widenedRound) |
|||
{ |
|||
output.V0 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit); |
|||
output.V1 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit); |
|||
output.V2 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit); |
|||
output.V3 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit); |
|||
return; |
|||
} |
|||
|
|||
output.V0 = Av1Transform1dMath.MultiplyAdd4(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit); |
|||
output.V1 = Av1Transform1dMath.MultiplyAdd4(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit); |
|||
output.V2 = Av1Transform1dMath.MultiplyAdd4(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit); |
|||
output.V3 = Av1Transform1dMath.MultiplyAdd4(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the inverse four-point matrix to eight independent axes.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for eight transform axes.</param>
|
|||
/// <param name="output">The destination values for eight transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
|
|||
/// <param name="widenedRound">Whether the terminal fixed-point rounding requires signed 64-bit lanes.</param>
|
|||
private static void TransformCore( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
int cosBit, |
|||
bool widenedRound) |
|||
{ |
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Vector256<int> x0 = input.V0; |
|||
Vector256<int> x1 = input.V1; |
|||
Vector256<int> x2 = input.V2; |
|||
Vector256<int> x3 = input.V3; |
|||
|
|||
if (widenedRound) |
|||
{ |
|||
output.V0 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit); |
|||
output.V1 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit); |
|||
output.V2 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit); |
|||
output.V3 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit); |
|||
return; |
|||
} |
|||
|
|||
output.V0 = Av1Transform1dMath.MultiplyAdd4(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit); |
|||
output.V1 = Av1Transform1dMath.MultiplyAdd4(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit); |
|||
output.V2 = Av1Transform1dMath.MultiplyAdd4(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit); |
|||
output.V3 = Av1Transform1dMath.MultiplyAdd4(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit); |
|||
} |
|||
} |
|||
@ -0,0 +1,289 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 inverse asymmetric discrete sine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Adst8Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative eight-point AV1 inverse asymmetric discrete sine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The eight frequency-domain coefficients.</param>
|
|||
/// <param name="output">The eight spatial-domain residual values.</param>
|
|||
/// <param name="step">The eight-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output[0] = input[7]; |
|||
output[1] = input[0]; |
|||
output[2] = input[5]; |
|||
output[3] = input[2]; |
|||
output[4] = input[3]; |
|||
output[5] = input[4]; |
|||
output[6] = input[1]; |
|||
output[7] = input[6]; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step[0] = Av1Transform1dMath.HalfButterfly(cospi[4], output[0], cospi[60], output[1], cosBit); |
|||
step[1] = Av1Transform1dMath.HalfButterfly(cospi[60], output[0], -cospi[4], output[1], cosBit); |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[20], output[2], cospi[44], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[44], output[2], -cospi[20], output[3], cosBit); |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[36], output[4], cospi[28], output[5], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[28], output[4], -cospi[36], output[5], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[52], output[6], cospi[12], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[12], output[6], -cospi[52], output[7], cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[4], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[5], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[6], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[7], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[0] - step[4], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[1] - step[5], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[2] - step[6], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[3] - step[7], stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[16], output[4], cospi[48], output[5], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[48], output[4], -cospi[16], output[5], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[6], cospi[16], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[16], output[6], cospi[48], output[7], cosBit); |
|||
|
|||
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[2], stageRange[stage]); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[3], stageRange[stage]); |
|||
output[2] = Av1Transform1dMath.Clamp(step[0] - step[2], stageRange[stage]); |
|||
output[3] = Av1Transform1dMath.Clamp(step[1] - step[3], stageRange[stage]); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[6], stageRange[stage]); |
|||
output[5] = Av1Transform1dMath.Clamp(step[5] + step[7], stageRange[stage]); |
|||
output[6] = Av1Transform1dMath.Clamp(step[4] - step[6], stageRange[stage]); |
|||
output[7] = Av1Transform1dMath.Clamp(step[5] - step[7], stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/4 rotations for the middle pairs.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], cospi[32], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], -cospi[32], output[3], cosBit); |
|||
step[4] = output[4]; |
|||
step[5] = output[5]; |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], cospi[32], output[7], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], -cospi[32], output[7], cosBit); |
|||
|
|||
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output[0] = step[0]; |
|||
output[1] = -step[4]; |
|||
output[2] = step[6]; |
|||
output[3] = -step[2]; |
|||
output[4] = step[3]; |
|||
output[5] = -step[7]; |
|||
output[6] = step[5]; |
|||
output[7] = -step[1]; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output.V0 = input.V7; |
|||
output.V1 = input.V0; |
|||
output.V2 = input.V5; |
|||
output.V3 = input.V2; |
|||
output.V4 = input.V3; |
|||
output.V5 = input.V4; |
|||
output.V6 = input.V1; |
|||
output.V7 = input.V6; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V0, cospi[60], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V0, -cospi[4], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V2, cospi[44], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V2, -cospi[20], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V4, cospi[28], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V4, -cospi[36], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V6, cospi[12], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V6, -cospi[52], output.V7, cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit); |
|||
|
|||
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/4 rotations for the middle pairs.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit); |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit); |
|||
|
|||
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output.V0 = step.V0; |
|||
output.V1 = -step.V4; |
|||
output.V2 = step.V6; |
|||
output.V3 = -step.V2; |
|||
output.V4 = step.V3; |
|||
output.V5 = -step.V7; |
|||
output.V6 = step.V5; |
|||
output.V7 = -step.V1; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
|
|||
stage++; |
|||
output.V0 = input.V7; |
|||
output.V1 = input.V0; |
|||
output.V2 = input.V5; |
|||
output.V3 = input.V2; |
|||
output.V4 = input.V3; |
|||
output.V5 = input.V4; |
|||
output.V6 = input.V1; |
|||
output.V7 = input.V6; |
|||
|
|||
// Stage 2 applies the terminal odd-angle rotations in reverse.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V0, cospi[60], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V0, -cospi[4], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V2, cospi[44], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V2, -cospi[20], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V4, cospi[28], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V4, -cospi[36], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V6, cospi[12], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V6, -cospi[52], output.V7, cosBit); |
|||
|
|||
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]); |
|||
|
|||
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit); |
|||
|
|||
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
|
|||
stage++; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]); |
|||
|
|||
// Stage 6 reverses the pi/4 rotations for the middle pairs.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit); |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit); |
|||
|
|||
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
|
|||
output.V0 = step.V0; |
|||
output.V1 = -step.V4; |
|||
output.V2 = step.V6; |
|||
output.V3 = -step.V2; |
|||
output.V4 = step.V3; |
|||
output.V5 = -step.V7; |
|||
output.V6 = step.V5; |
|||
output.V7 = -step.V1; |
|||
} |
|||
} |
|||
@ -0,0 +1,475 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the 16-point AV1 inverse discrete cosine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Dct16Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative 16-point AV1 inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The sixteen frequency-domain coefficients.</param>
|
|||
/// <param name="output">The sixteen spatial-domain residual values.</param>
|
|||
/// <param name="step">The sixteen-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output[0] = input[0]; |
|||
output[1] = input[8]; |
|||
output[2] = input[4]; |
|||
output[3] = input[12]; |
|||
output[4] = input[2]; |
|||
output[5] = input[10]; |
|||
output[6] = input[6]; |
|||
output[7] = input[14]; |
|||
output[8] = input[1]; |
|||
output[9] = input[9]; |
|||
output[10] = input[5]; |
|||
output[11] = input[13]; |
|||
output[12] = input[3]; |
|||
output[13] = input[11]; |
|||
output[14] = input[7]; |
|||
output[15] = input[15]; |
|||
|
|||
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = output[4]; |
|||
step[5] = output[5]; |
|||
step[6] = output[6]; |
|||
step[7] = output[7]; |
|||
step[8] = Av1Transform1dMath.HalfButterfly(cospi[60], output[8], -cospi[4], output[15], cosBit); |
|||
step[9] = Av1Transform1dMath.HalfButterfly(cospi[28], output[9], -cospi[36], output[14], cosBit); |
|||
step[10] = Av1Transform1dMath.HalfButterfly(cospi[44], output[10], -cospi[20], output[13], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(cospi[12], output[11], -cospi[52], output[12], cosBit); |
|||
step[12] = Av1Transform1dMath.HalfButterfly(cospi[52], output[11], cospi[12], output[12], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[20], output[10], cospi[44], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(cospi[36], output[9], cospi[28], output[14], cosBit); |
|||
step[15] = Av1Transform1dMath.HalfButterfly(cospi[4], output[8], cospi[60], output[15], cosBit); |
|||
|
|||
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output[0] = step[0]; |
|||
output[1] = step[1]; |
|||
output[2] = step[2]; |
|||
output[3] = step[3]; |
|||
output[4] = Av1Transform1dMath.HalfButterfly(cospi[56], step[4], -cospi[8], step[7], cosBit); |
|||
output[5] = Av1Transform1dMath.HalfButterfly(cospi[24], step[5], -cospi[40], step[6], cosBit); |
|||
output[6] = Av1Transform1dMath.HalfButterfly(cospi[40], step[5], cospi[24], step[6], cosBit); |
|||
output[7] = Av1Transform1dMath.HalfButterfly(cospi[8], step[4], cospi[56], step[7], cosBit); |
|||
output[8] = Av1Transform1dMath.Clamp(step[8] + step[9], range); |
|||
output[9] = Av1Transform1dMath.Clamp(step[8] - step[9], range); |
|||
output[10] = Av1Transform1dMath.Clamp(step[11] - step[10], range); |
|||
output[11] = Av1Transform1dMath.Clamp(step[10] + step[11], range); |
|||
output[12] = Av1Transform1dMath.Clamp(step[12] + step[13], range); |
|||
output[13] = Av1Transform1dMath.Clamp(step[12] - step[13], range); |
|||
output[14] = Av1Transform1dMath.Clamp(step[15] - step[14], range); |
|||
output[15] = Av1Transform1dMath.Clamp(step[14] + step[15], range); |
|||
|
|||
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step[0] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], cospi[32], output[1], cosBit); |
|||
step[1] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], -cospi[32], output[1], cosBit); |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[48], output[2], -cospi[16], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[16], output[2], cospi[48], output[3], cosBit); |
|||
step[4] = Av1Transform1dMath.Clamp(output[4] + output[5], range); |
|||
step[5] = Av1Transform1dMath.Clamp(output[4] - output[5], range); |
|||
step[6] = Av1Transform1dMath.Clamp(output[7] - output[6], range); |
|||
step[7] = Av1Transform1dMath.Clamp(output[6] + output[7], range); |
|||
step[8] = output[8]; |
|||
step[9] = Av1Transform1dMath.HalfButterfly(-cospi[16], output[9], cospi[48], output[14], cosBit); |
|||
step[10] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[10], -cospi[16], output[13], cosBit); |
|||
step[11] = output[11]; |
|||
step[12] = output[12]; |
|||
step[13] = Av1Transform1dMath.HalfButterfly(-cospi[16], output[10], cospi[48], output[13], cosBit); |
|||
step[14] = Av1Transform1dMath.HalfButterfly(cospi[48], output[9], cospi[16], output[14], cosBit); |
|||
step[15] = output[15]; |
|||
|
|||
// Stage 5 widens the reconstructed groups through their next butterfly level.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[3], range); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[2], range); |
|||
output[2] = Av1Transform1dMath.Clamp(step[1] - step[2], range); |
|||
output[3] = Av1Transform1dMath.Clamp(step[0] - step[3], range); |
|||
output[4] = step[4]; |
|||
output[5] = Av1Transform1dMath.HalfButterfly(-cospi[32], step[5], cospi[32], step[6], cosBit); |
|||
output[6] = Av1Transform1dMath.HalfButterfly(cospi[32], step[5], cospi[32], step[6], cosBit); |
|||
output[7] = step[7]; |
|||
output[8] = Av1Transform1dMath.Clamp(step[8] + step[11], range); |
|||
output[9] = Av1Transform1dMath.Clamp(step[9] + step[10], range); |
|||
output[10] = Av1Transform1dMath.Clamp(step[9] - step[10], range); |
|||
output[11] = Av1Transform1dMath.Clamp(step[8] - step[11], range); |
|||
output[12] = Av1Transform1dMath.Clamp(step[15] - step[12], range); |
|||
output[13] = Av1Transform1dMath.Clamp(step[14] - step[13], range); |
|||
output[14] = Av1Transform1dMath.Clamp(step[13] + step[14], range); |
|||
output[15] = Av1Transform1dMath.Clamp(step[12] + step[15], range); |
|||
|
|||
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step[0] = Av1Transform1dMath.Clamp(output[0] + output[7], range); |
|||
step[1] = Av1Transform1dMath.Clamp(output[1] + output[6], range); |
|||
step[2] = Av1Transform1dMath.Clamp(output[2] + output[5], range); |
|||
step[3] = Av1Transform1dMath.Clamp(output[3] + output[4], range); |
|||
step[4] = Av1Transform1dMath.Clamp(output[3] - output[4], range); |
|||
step[5] = Av1Transform1dMath.Clamp(output[2] - output[5], range); |
|||
step[6] = Av1Transform1dMath.Clamp(output[1] - output[6], range); |
|||
step[7] = Av1Transform1dMath.Clamp(output[0] - output[7], range); |
|||
step[8] = output[8]; |
|||
step[9] = output[9]; |
|||
step[10] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[10], cospi[32], output[13], cosBit); |
|||
step[11] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[11], cospi[32], output[12], cosBit); |
|||
step[12] = Av1Transform1dMath.HalfButterfly(cospi[32], output[11], cospi[32], output[12], cosBit); |
|||
step[13] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], cospi[32], output[13], cosBit); |
|||
step[14] = output[14]; |
|||
step[15] = output[15]; |
|||
|
|||
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[15], range); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[14], range); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[13], range); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[12], range); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[11], range); |
|||
output[5] = Av1Transform1dMath.Clamp(step[5] + step[10], range); |
|||
output[6] = Av1Transform1dMath.Clamp(step[6] + step[9], range); |
|||
output[7] = Av1Transform1dMath.Clamp(step[7] + step[8], range); |
|||
output[8] = Av1Transform1dMath.Clamp(step[7] - step[8], range); |
|||
output[9] = Av1Transform1dMath.Clamp(step[6] - step[9], range); |
|||
output[10] = Av1Transform1dMath.Clamp(step[5] - step[10], range); |
|||
output[11] = Av1Transform1dMath.Clamp(step[4] - step[11], range); |
|||
output[12] = Av1Transform1dMath.Clamp(step[3] - step[12], range); |
|||
output[13] = Av1Transform1dMath.Clamp(step[2] - step[13], range); |
|||
output[14] = Av1Transform1dMath.Clamp(step[1] - step[14], range); |
|||
output[15] = Av1Transform1dMath.Clamp(step[0] - step[15], range); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output.V0 = input.V0; |
|||
output.V1 = input.V8; |
|||
output.V2 = input.V4; |
|||
output.V3 = input.V12; |
|||
output.V4 = input.V2; |
|||
output.V5 = input.V10; |
|||
output.V6 = input.V6; |
|||
output.V7 = input.V14; |
|||
output.V8 = input.V1; |
|||
output.V9 = input.V9; |
|||
output.V10 = input.V5; |
|||
output.V11 = input.V13; |
|||
output.V12 = input.V3; |
|||
output.V13 = input.V11; |
|||
output.V14 = input.V7; |
|||
output.V15 = input.V15; |
|||
|
|||
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = output.V6; |
|||
step.V7 = output.V7; |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V8, -cospi[4], output.V15, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V9, -cospi[36], output.V14, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V10, -cospi[20], output.V13, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V11, -cospi[52], output.V12, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V11, cospi[12], output.V12, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V10, cospi[44], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V9, cospi[28], output.V14, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V8, cospi[60], output.V15, cosBit); |
|||
|
|||
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output.V0 = step.V0; |
|||
output.V1 = step.V1; |
|||
output.V2 = step.V2; |
|||
output.V3 = step.V3; |
|||
output.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], step.V4, -cospi[8], step.V7, cosBit); |
|||
output.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], step.V5, -cospi[40], step.V6, cosBit); |
|||
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], step.V5, cospi[24], step.V6, cosBit); |
|||
output.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], step.V4, cospi[56], step.V7, cosBit); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V9, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V8 - step.V9, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V11 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V10 + step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V13, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V12 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V15 - step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V14 + step.V15, range); |
|||
|
|||
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.Clamp(output.V4 + output.V5, range); |
|||
step.V5 = Av1Transform1dMath.Clamp(output.V4 - output.V5, range); |
|||
step.V6 = Av1Transform1dMath.Clamp(output.V7 - output.V6, range); |
|||
step.V7 = Av1Transform1dMath.Clamp(output.V6 + output.V7, range); |
|||
step.V8 = output.V8; |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V9, cospi[48], output.V14, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V10, -cospi[16], output.V13, cosBit); |
|||
step.V11 = output.V11; |
|||
step.V12 = output.V12; |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V10, cospi[48], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V9, cospi[16], output.V14, cosBit); |
|||
step.V15 = output.V15; |
|||
|
|||
// Stage 5 widens the reconstructed groups through their next butterfly level.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range); |
|||
output.V4 = step.V4; |
|||
output.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], step.V5, cospi[32], step.V6, cosBit); |
|||
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V5, cospi[32], step.V6, cosBit); |
|||
output.V7 = step.V7; |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V11, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V10, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V9 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V8 - step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V15 - step.V12, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V14 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V13 + step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V12 + step.V15, range); |
|||
|
|||
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V7, range); |
|||
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V6, range); |
|||
step.V2 = Av1Transform1dMath.Clamp(output.V2 + output.V5, range); |
|||
step.V3 = Av1Transform1dMath.Clamp(output.V3 + output.V4, range); |
|||
step.V4 = Av1Transform1dMath.Clamp(output.V3 - output.V4, range); |
|||
step.V5 = Av1Transform1dMath.Clamp(output.V2 - output.V5, range); |
|||
step.V6 = Av1Transform1dMath.Clamp(output.V1 - output.V6, range); |
|||
step.V7 = Av1Transform1dMath.Clamp(output.V0 - output.V7, range); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V10, cospi[32], output.V13, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V11, cospi[32], output.V12, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V11, cospi[32], output.V12, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V13, cosBit); |
|||
step.V14 = output.V14; |
|||
step.V15 = output.V15; |
|||
|
|||
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V15, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V14, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V13, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V12, range); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V11, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V10, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V9, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V8, range); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V7 - step.V8, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V6 - step.V9, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V5 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V4 - step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V3 - step.V12, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V2 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V1 - step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V0 - step.V15, range); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output.V0 = input.V0; |
|||
output.V1 = input.V8; |
|||
output.V2 = input.V4; |
|||
output.V3 = input.V12; |
|||
output.V4 = input.V2; |
|||
output.V5 = input.V10; |
|||
output.V6 = input.V6; |
|||
output.V7 = input.V14; |
|||
output.V8 = input.V1; |
|||
output.V9 = input.V9; |
|||
output.V10 = input.V5; |
|||
output.V11 = input.V13; |
|||
output.V12 = input.V3; |
|||
output.V13 = input.V11; |
|||
output.V14 = input.V7; |
|||
output.V15 = input.V15; |
|||
|
|||
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = output.V4; |
|||
step.V5 = output.V5; |
|||
step.V6 = output.V6; |
|||
step.V7 = output.V7; |
|||
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V8, -cospi[4], output.V15, cosBit); |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V9, -cospi[36], output.V14, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V10, -cospi[20], output.V13, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V11, -cospi[52], output.V12, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V11, cospi[12], output.V12, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V10, cospi[44], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V9, cospi[28], output.V14, cosBit); |
|||
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V8, cospi[60], output.V15, cosBit); |
|||
|
|||
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output.V0 = step.V0; |
|||
output.V1 = step.V1; |
|||
output.V2 = step.V2; |
|||
output.V3 = step.V3; |
|||
output.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], step.V4, -cospi[8], step.V7, cosBit); |
|||
output.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], step.V5, -cospi[40], step.V6, cosBit); |
|||
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], step.V5, cospi[24], step.V6, cosBit); |
|||
output.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], step.V4, cospi[56], step.V7, cosBit); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V9, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V8 - step.V9, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V11 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V10 + step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V13, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V12 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V15 - step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V14 + step.V15, range); |
|||
|
|||
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit); |
|||
step.V4 = Av1Transform1dMath.Clamp(output.V4 + output.V5, range); |
|||
step.V5 = Av1Transform1dMath.Clamp(output.V4 - output.V5, range); |
|||
step.V6 = Av1Transform1dMath.Clamp(output.V7 - output.V6, range); |
|||
step.V7 = Av1Transform1dMath.Clamp(output.V6 + output.V7, range); |
|||
step.V8 = output.V8; |
|||
step.V9 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V9, cospi[48], output.V14, cosBit); |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V10, -cospi[16], output.V13, cosBit); |
|||
step.V11 = output.V11; |
|||
step.V12 = output.V12; |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V10, cospi[48], output.V13, cosBit); |
|||
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V9, cospi[16], output.V14, cosBit); |
|||
step.V15 = output.V15; |
|||
|
|||
// Stage 5 widens the reconstructed groups through their next butterfly level.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range); |
|||
output.V4 = step.V4; |
|||
output.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], step.V5, cospi[32], step.V6, cosBit); |
|||
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V5, cospi[32], step.V6, cosBit); |
|||
output.V7 = step.V7; |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V11, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V10, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V9 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V8 - step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V15 - step.V12, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V14 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V13 + step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V12 + step.V15, range); |
|||
|
|||
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V7, range); |
|||
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V6, range); |
|||
step.V2 = Av1Transform1dMath.Clamp(output.V2 + output.V5, range); |
|||
step.V3 = Av1Transform1dMath.Clamp(output.V3 + output.V4, range); |
|||
step.V4 = Av1Transform1dMath.Clamp(output.V3 - output.V4, range); |
|||
step.V5 = Av1Transform1dMath.Clamp(output.V2 - output.V5, range); |
|||
step.V6 = Av1Transform1dMath.Clamp(output.V1 - output.V6, range); |
|||
step.V7 = Av1Transform1dMath.Clamp(output.V0 - output.V7, range); |
|||
step.V8 = output.V8; |
|||
step.V9 = output.V9; |
|||
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V10, cospi[32], output.V13, cosBit); |
|||
step.V11 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V11, cospi[32], output.V12, cosBit); |
|||
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V11, cospi[32], output.V12, cosBit); |
|||
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V13, cosBit); |
|||
step.V14 = output.V14; |
|||
step.V15 = output.V15; |
|||
|
|||
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V15, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V14, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V13, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V12, range); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V11, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V10, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V9, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V8, range); |
|||
output.V8 = Av1Transform1dMath.Clamp(step.V7 - step.V8, range); |
|||
output.V9 = Av1Transform1dMath.Clamp(step.V6 - step.V9, range); |
|||
output.V10 = Av1Transform1dMath.Clamp(step.V5 - step.V10, range); |
|||
output.V11 = Av1Transform1dMath.Clamp(step.V4 - step.V11, range); |
|||
output.V12 = Av1Transform1dMath.Clamp(step.V3 - step.V12, range); |
|||
output.V13 = Av1Transform1dMath.Clamp(step.V2 - step.V13, range); |
|||
output.V14 = Av1Transform1dMath.Clamp(step.V1 - step.V14, range); |
|||
output.V15 = Av1Transform1dMath.Clamp(step.V0 - step.V15, range); |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,112 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the four-point AV1 inverse discrete cosine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Dct4Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative four-point AV1 inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The four frequency-domain coefficients.</param>
|
|||
/// <param name="output">The four spatial-domain residual values.</param>
|
|||
/// <param name="step">The four-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
|
|||
output[0] = input[0]; |
|||
output[1] = input[2]; |
|||
output[2] = input[1]; |
|||
output[3] = input[3]; |
|||
|
|||
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
|
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
step[0] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], cospi[32], output[1], cosBit); |
|||
step[1] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], -cospi[32], output[1], cosBit); |
|||
step[2] = Av1Transform1dMath.HalfButterfly(cospi[48], output[2], -cospi[16], output[3], cosBit); |
|||
step[3] = Av1Transform1dMath.HalfButterfly(cospi[16], output[2], cospi[48], output[3], cosBit); |
|||
|
|||
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
|
|||
byte range = stageRange[3]; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[3], range); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[2], range); |
|||
output[2] = Av1Transform1dMath.Clamp(step[1] - step[2], range); |
|||
output[3] = Av1Transform1dMath.Clamp(step[0] - step[3], range); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
|
|||
output.V0 = input.V0; |
|||
output.V1 = input.V2; |
|||
output.V2 = input.V1; |
|||
output.V3 = input.V3; |
|||
|
|||
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
|
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit); |
|||
|
|||
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
|
|||
byte range = stageRange[3]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
|
|||
output.V0 = input.V0; |
|||
output.V1 = input.V2; |
|||
output.V2 = input.V1; |
|||
output.V3 = input.V3; |
|||
|
|||
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
|
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit); |
|||
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit); |
|||
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit); |
|||
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit); |
|||
|
|||
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
|
|||
byte range = stageRange[3]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range); |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,232 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 inverse discrete cosine transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
|
|||
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Dct8Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative eight-point AV1 inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The eight frequency-domain coefficients.</param>
|
|||
/// <param name="output">The eight spatial-domain residual values.</param>
|
|||
/// <param name="step">The eight-element stage buffer owned by the containing two-dimensional transform.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output[0] = input[0]; |
|||
output[1] = input[4]; |
|||
output[2] = input[2]; |
|||
output[3] = input[6]; |
|||
output[4] = input[1]; |
|||
output[5] = input[5]; |
|||
output[6] = input[3]; |
|||
output[7] = input[7]; |
|||
|
|||
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
|
|||
stage++; |
|||
step[0] = output[0]; |
|||
step[1] = output[1]; |
|||
step[2] = output[2]; |
|||
step[3] = output[3]; |
|||
step[4] = Av1Transform1dMath.HalfButterfly(cospi[56], output[4], -cospi[8], output[7], cosBit); |
|||
step[5] = Av1Transform1dMath.HalfButterfly(cospi[24], output[5], -cospi[40], output[6], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[40], output[5], cospi[24], output[6], cosBit); |
|||
step[7] = Av1Transform1dMath.HalfButterfly(cospi[8], output[4], cospi[56], output[7], cosBit); |
|||
|
|||
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output[0] = Av1Transform1dMath.HalfButterfly(cospi[32], step[0], cospi[32], step[1], cosBit); |
|||
output[1] = Av1Transform1dMath.HalfButterfly(cospi[32], step[0], -cospi[32], step[1], cosBit); |
|||
output[2] = Av1Transform1dMath.HalfButterfly(cospi[48], step[2], -cospi[16], step[3], cosBit); |
|||
output[3] = Av1Transform1dMath.HalfButterfly(cospi[16], step[2], cospi[48], step[3], cosBit); |
|||
output[4] = Av1Transform1dMath.Clamp(step[4] + step[5], range); |
|||
output[5] = Av1Transform1dMath.Clamp(step[4] - step[5], range); |
|||
output[6] = Av1Transform1dMath.Clamp(step[7] - step[6], range); |
|||
output[7] = Av1Transform1dMath.Clamp(step[6] + step[7], range); |
|||
|
|||
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
|
|||
stage++; |
|||
step[0] = Av1Transform1dMath.Clamp(output[0] + output[3], range); |
|||
step[1] = Av1Transform1dMath.Clamp(output[1] + output[2], range); |
|||
step[2] = Av1Transform1dMath.Clamp(output[1] - output[2], range); |
|||
step[3] = Av1Transform1dMath.Clamp(output[0] - output[3], range); |
|||
step[4] = output[4]; |
|||
step[5] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[5], cospi[32], output[6], cosBit); |
|||
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[5], cospi[32], output[6], cosBit); |
|||
step[7] = output[7]; |
|||
|
|||
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output[0] = Av1Transform1dMath.Clamp(step[0] + step[7], range); |
|||
output[1] = Av1Transform1dMath.Clamp(step[1] + step[6], range); |
|||
output[2] = Av1Transform1dMath.Clamp(step[2] + step[5], range); |
|||
output[3] = Av1Transform1dMath.Clamp(step[3] + step[4], range); |
|||
output[4] = Av1Transform1dMath.Clamp(step[3] - step[4], range); |
|||
output[5] = Av1Transform1dMath.Clamp(step[2] - step[5], range); |
|||
output[6] = Av1Transform1dMath.Clamp(step[1] - step[6], range); |
|||
output[7] = Av1Transform1dMath.Clamp(step[0] - step[7], range); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output.V0 = input.V0; |
|||
output.V1 = input.V4; |
|||
output.V2 = input.V2; |
|||
output.V3 = input.V6; |
|||
output.V4 = input.V1; |
|||
output.V5 = input.V5; |
|||
output.V6 = input.V3; |
|||
output.V7 = input.V7; |
|||
|
|||
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V4, -cospi[8], output.V7, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V5, -cospi[40], output.V6, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V5, cospi[24], output.V6, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V4, cospi[56], output.V7, cosBit); |
|||
|
|||
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, cospi[32], step.V1, cosBit); |
|||
output.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, -cospi[32], step.V1, cosBit); |
|||
output.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], step.V2, -cospi[16], step.V3, cosBit); |
|||
output.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], step.V2, cospi[48], step.V3, cosBit); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V5, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V4 - step.V5, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V7 - step.V6, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V6 + step.V7, range); |
|||
|
|||
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V3, range); |
|||
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V2, range); |
|||
step.V2 = Av1Transform1dMath.Clamp(output.V1 - output.V2, range); |
|||
step.V3 = Av1Transform1dMath.Clamp(output.V0 - output.V3, range); |
|||
step.V4 = output.V4; |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V5, cospi[32], output.V6, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V5, cospi[32], output.V6, cosBit); |
|||
step.V7 = output.V7; |
|||
|
|||
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V7, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V6, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V5, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V4, range); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V3 - step.V4, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V2 - step.V5, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V1 - step.V6, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V0 - step.V7, range); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the transform to four independent axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="input">The source values for the parallel transform axes.</param>
|
|||
/// <param name="output">The destination values for the parallel transform axes.</param>
|
|||
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
|
|||
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
|
|||
stage++; |
|||
output.V0 = input.V0; |
|||
output.V1 = input.V4; |
|||
output.V2 = input.V2; |
|||
output.V3 = input.V6; |
|||
output.V4 = input.V1; |
|||
output.V5 = input.V5; |
|||
output.V6 = input.V3; |
|||
output.V7 = input.V7; |
|||
|
|||
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
|
|||
stage++; |
|||
step.V0 = output.V0; |
|||
step.V1 = output.V1; |
|||
step.V2 = output.V2; |
|||
step.V3 = output.V3; |
|||
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V4, -cospi[8], output.V7, cosBit); |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V5, -cospi[40], output.V6, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V5, cospi[24], output.V6, cosBit); |
|||
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V4, cospi[56], output.V7, cosBit); |
|||
|
|||
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
|
|||
stage++; |
|||
byte range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, cospi[32], step.V1, cosBit); |
|||
output.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, -cospi[32], step.V1, cosBit); |
|||
output.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], step.V2, -cospi[16], step.V3, cosBit); |
|||
output.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], step.V2, cospi[48], step.V3, cosBit); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V5, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V4 - step.V5, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V7 - step.V6, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V6 + step.V7, range); |
|||
|
|||
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
|
|||
stage++; |
|||
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V3, range); |
|||
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V2, range); |
|||
step.V2 = Av1Transform1dMath.Clamp(output.V1 - output.V2, range); |
|||
step.V3 = Av1Transform1dMath.Clamp(output.V0 - output.V3, range); |
|||
step.V4 = output.V4; |
|||
step.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V5, cospi[32], output.V6, cosBit); |
|||
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V5, cospi[32], output.V6, cosBit); |
|||
step.V7 = output.V7; |
|||
|
|||
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
|
|||
stage++; |
|||
range = stageRange[stage]; |
|||
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V7, range); |
|||
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V6, range); |
|||
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V5, range); |
|||
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V4, range); |
|||
output.V4 = Av1Transform1dMath.Clamp(step.V3 - step.V4, range); |
|||
output.V5 = Av1Transform1dMath.Clamp(step.V2 - step.V5, range); |
|||
output.V6 = Av1Transform1dMath.Clamp(step.V1 - step.V6, range); |
|||
output.V7 = Av1Transform1dMath.Clamp(step.V0 - step.V7, range); |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the sixteen-point AV1 inverse identity transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
|
|||
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Identity16Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative sixteen-point AV1 inverse identity transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The sixteen frequency-domain coefficients.</param>
|
|||
/// <param name="output">The sixteen scaled spatial-domain values.</param>
|
|||
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
|
|||
// The AV1 identity transform preserves coefficient order while applying the twice the square-root-of-two fixed-point scale required for 2-D normalization.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
output[i] = Av1Math.RoundShift((long)input[i] * (2 * Av1Transform1dMath.NewSqrt2), Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
// The doubled scale exceeds Int32 only for the 20-bit twelve-bit row range. Widen that exact product and
|
|||
// rounding sequence, matching libaom without changing the established lower-range SIMD path.
|
|||
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount) |
|||
{ |
|||
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
else |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
|
|||
_ = step; |
|||
_ = cosBit; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount) |
|||
{ |
|||
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
else |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
|
|||
_ = step; |
|||
_ = cosBit; |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the thirty-two-point AV1 inverse identity transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
|
|||
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Identity32Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative thirty-two-point AV1 inverse identity transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The thirty-two frequency-domain coefficients.</param>
|
|||
/// <param name="output">The thirty-two scaled spatial-domain values.</param>
|
|||
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
|
|||
// The AV1 identity transform preserves coefficient order while applying the exact factor-of-four scale required for 2-D normalization.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
output[i] = input[i] * 4; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0); |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0); |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the four-point AV1 inverse identity transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
|
|||
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Identity4Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative four-point AV1 inverse identity transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The four frequency-domain coefficients.</param>
|
|||
/// <param name="output">The four scaled spatial-domain values.</param>
|
|||
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
|
|||
// The AV1 identity transform preserves coefficient order while applying the square-root-of-two fixed-point scale required for 2-D normalization.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
output[i] = Av1Math.RoundShift((long)input[i] * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
// Only a twelve-bit row transform has the 20-bit input range that can overflow this fixed-point product.
|
|||
// Match libaom's high-bit-depth kernel there while retaining the compact Int32 path for narrower ranges.
|
|||
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount) |
|||
{ |
|||
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
else |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
|
|||
_ = step; |
|||
_ = cosBit; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount) |
|||
{ |
|||
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
else |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits); |
|||
} |
|||
|
|||
_ = step; |
|||
_ = cosBit; |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
/// <summary>
|
|||
/// Defines the eight-point AV1 inverse identity transform operator.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
|
|||
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
|
|||
/// </remarks>
|
|||
internal readonly struct Av1Identity8Inverse1dOperator : IAv1Transform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the normative eight-point AV1 inverse identity transform.
|
|||
/// </summary>
|
|||
/// <param name="input">The eight frequency-domain coefficients.</param>
|
|||
/// <param name="output">The eight scaled spatial-domain values.</param>
|
|||
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
|
|||
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
|
|||
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange) |
|||
{ |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
|
|||
// The AV1 identity transform preserves coefficient order while applying the exact factor-of-two scale required for 2-D normalization.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
output[i] = input[i] * 2; |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector128<int>> input, |
|||
ref Av1TransformVector<Vector128<int>> output, |
|||
ref Av1TransformVector<Vector128<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0); |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref Av1TransformVector<Vector256<int>> input, |
|||
ref Av1TransformVector<Vector256<int>> output, |
|||
ref Av1TransformVector<Vector256<int>> step, |
|||
int cosBit, |
|||
Av1TransformStageRange stageRange) |
|||
{ |
|||
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0); |
|||
_ = step; |
|||
_ = cosBit; |
|||
_ = stageRange; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue