mirror of https://github.com/SixLabors/ImageSharp
110 changed files with 18102 additions and 9158 deletions
@ -0,0 +1,34 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef; |
|||
|
|||
internal static partial class Av1CdefFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes filtered samples to eight-bit plane storage.
|
|||
/// </summary>
|
|||
private readonly struct ByteOutputOperator : IOutputOperator<byte> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void StoreVector(ref byte destination, int offset, Vector128<short> value, int count) |
|||
{ |
|||
Vector64<byte> packed = Vector128.Narrow(value.AsUInt16(), Vector128<ushort>.Zero).GetLower(); |
|||
ref byte output = ref Unsafe.Add(ref destination, offset); |
|||
if (count == 8) |
|||
{ |
|||
packed.StoreUnsafe(ref output); |
|||
} |
|||
else |
|||
{ |
|||
Unsafe.WriteUnaligned(ref output, packed.AsUInt32().ToScalar()); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void StoreScalar(ref byte destination, int offset, int value) => Unsafe.Add(ref destination, offset) = (byte)value; |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef; |
|||
|
|||
internal static partial class Av1CdefFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Disables both tap groups so the source block is copied unchanged.
|
|||
/// </summary>
|
|||
private readonly struct CopyFilterOperator : IFilterOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static bool EnablePrimary => false; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static bool EnableSecondary => false; |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef; |
|||
|
|||
internal static partial class Av1CdefFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Defines storage-specific writes for one filtered row.
|
|||
/// </summary>
|
|||
/// <typeparam name="TSample">The destination sample storage type.</typeparam>
|
|||
private interface IOutputOperator<TSample> |
|||
where TSample : unmanaged |
|||
{ |
|||
/// <summary>
|
|||
/// Stores four or eight filtered samples from the low vector lanes.
|
|||
/// </summary>
|
|||
/// <param name="destination">The first element in the destination plane.</param>
|
|||
/// <param name="offset">The offset of the first sample to write.</param>
|
|||
/// <param name="value">The filtered samples in the low lanes.</param>
|
|||
/// <param name="count">The number of valid lanes.</param>
|
|||
public static abstract void StoreVector(ref TSample destination, int offset, Vector128<short> value, int count); |
|||
|
|||
/// <summary>
|
|||
/// Stores one filtered sample.
|
|||
/// </summary>
|
|||
/// <param name="destination">The first element in the destination plane.</param>
|
|||
/// <param name="offset">The offset of the sample to write.</param>
|
|||
/// <param name="value">The filtered sample.</param>
|
|||
public static abstract void StoreScalar(ref TSample destination, int offset, int value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Defines which groups of directional taps participate in one closed filter kernel.
|
|||
/// </summary>
|
|||
private interface IFilterOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Gets a value indicating whether the primary directional taps are enabled.
|
|||
/// </summary>
|
|||
public static abstract bool EnablePrimary { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets a value indicating whether the secondary off-axis taps are enabled.
|
|||
/// </summary>
|
|||
public static abstract bool EnableSecondary { get; } |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef; |
|||
|
|||
internal static partial class Av1CdefFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Enables both directional tap groups and their combined clipping rule.
|
|||
/// </summary>
|
|||
private readonly struct PrimaryAndSecondaryFilterOperator : IFilterOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static bool EnablePrimary => true; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static bool EnableSecondary => true; |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef; |
|||
|
|||
internal static partial class Av1CdefFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Enables only the primary directional taps.
|
|||
/// </summary>
|
|||
private readonly struct PrimaryFilterOperator : IFilterOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static bool EnablePrimary => true; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static bool EnableSecondary => false; |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef; |
|||
|
|||
internal static partial class Av1CdefFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Enables only the secondary off-axis taps.
|
|||
/// </summary>
|
|||
private readonly struct SecondaryFilterOperator : IFilterOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static bool EnablePrimary => false; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static bool EnableSecondary => true; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.Cdef; |
|||
|
|||
internal static partial class Av1CdefFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Writes filtered samples to 16-bit plane storage.
|
|||
/// </summary>
|
|||
private readonly struct UInt16OutputOperator : IOutputOperator<ushort> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void StoreVector(ref ushort destination, int offset, Vector128<short> value, int count) |
|||
{ |
|||
ref ushort output = ref Unsafe.Add(ref destination, offset); |
|||
if (count == 8) |
|||
{ |
|||
value.AsUInt16().StoreUnsafe(ref output); |
|||
} |
|||
else |
|||
{ |
|||
ref byte outputBytes = ref Unsafe.As<ushort, byte>(ref output); |
|||
Unsafe.WriteUnaligned(ref outputBytes, value.AsUInt64().GetLower().ToScalar()); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void StoreScalar(ref ushort destination, int offset, int value) => Unsafe.Add(ref destination, offset) = (ushort)value; |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter; |
|||
|
|||
internal static partial class Av1DeblockingFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Accesses four columns across a horizontal edge in eight-bit storage.
|
|||
/// </summary>
|
|||
private readonly struct HorizontalByteEdgeOperator : IEdgeOperator<byte> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector128<int> LoadVector(ref byte samples, int q0Offset, int stride, int distance) |
|||
{ |
|||
ref byte source = ref Unsafe.Add(ref samples, q0Offset + (distance * stride)); |
|||
uint packed = Unsafe.ReadUnaligned<uint>(ref source); |
|||
Vector128<ushort> widened = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte()); |
|||
return Vector128.WidenLower(widened).AsInt32(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreVector(ref byte samples, int q0Offset, int stride, int distance, Vector128<int> value) |
|||
{ |
|||
Vector128<ushort> narrowed16 = Vector128.Narrow(value.AsUInt32(), Vector128<uint>.Zero); |
|||
Vector128<byte> narrowed8 = Vector128.Narrow(narrowed16, Vector128<ushort>.Zero); |
|||
Unsafe.WriteUnaligned(ref Unsafe.Add(ref samples, q0Offset + (distance * stride)), narrowed8.AsUInt32().ToScalar()); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int LoadScalar(ref byte samples, int q0Offset, int stride, int distance, int index) |
|||
=> Unsafe.Add(ref samples, q0Offset + (distance * stride) + index); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreScalar(ref byte samples, int q0Offset, int stride, int distance, int index, int value) |
|||
=> Unsafe.Add(ref samples, q0Offset + (distance * stride) + index) = (byte)value; |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter; |
|||
|
|||
internal static partial class Av1DeblockingFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Accesses four columns across a horizontal edge in 16-bit storage.
|
|||
/// </summary>
|
|||
private readonly struct HorizontalUInt16EdgeOperator : IEdgeOperator<ushort> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector128<int> LoadVector(ref ushort samples, int q0Offset, int stride, int distance) |
|||
{ |
|||
ref ushort source = ref Unsafe.Add(ref samples, q0Offset + (distance * stride)); |
|||
ulong packed = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<ushort, byte>(ref source)); |
|||
return Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsUInt16()).AsInt32(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreVector(ref ushort samples, int q0Offset, int stride, int distance, Vector128<int> value) |
|||
{ |
|||
Vector64<ushort> narrowed = Vector128.Narrow(value, Vector128<int>.Zero).AsUInt16().GetLower(); |
|||
ref byte destination = ref Unsafe.As<ushort, byte>(ref Unsafe.Add(ref samples, q0Offset + (distance * stride))); |
|||
Unsafe.WriteUnaligned(ref destination, narrowed.AsUInt64().ToScalar()); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int LoadScalar(ref ushort samples, int q0Offset, int stride, int distance, int index) |
|||
=> Unsafe.Add(ref samples, q0Offset + (distance * stride) + index); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreScalar(ref ushort samples, int q0Offset, int stride, int distance, int index, int value) |
|||
=> Unsafe.Add(ref samples, q0Offset + (distance * stride) + index) = (ushort)value; |
|||
} |
|||
} |
|||
@ -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.Pipeline.LoopFilter; |
|||
|
|||
internal static partial class Av1DeblockingFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Defines orientation- and storage-specific access to the four samples running along one edge segment.
|
|||
/// </summary>
|
|||
/// <typeparam name="TSample">The reconstructed sample storage type.</typeparam>
|
|||
private interface IEdgeOperator<TSample> |
|||
where TSample : unmanaged |
|||
{ |
|||
/// <summary>
|
|||
/// Loads four samples at one signed distance across the edge.
|
|||
/// </summary>
|
|||
/// <param name="samples">The first element in the plane storage.</param>
|
|||
/// <param name="q0Offset">The offset of the first Q-side sample.</param>
|
|||
/// <param name="stride">The number of samples between adjacent rows.</param>
|
|||
/// <param name="distance">The signed sample distance from Q0.</param>
|
|||
/// <returns>The widened samples ordered along the edge.</returns>
|
|||
public static abstract Vector128<int> LoadVector(ref TSample samples, int q0Offset, int stride, int distance); |
|||
|
|||
/// <summary>
|
|||
/// Stores four samples at one signed distance across the edge.
|
|||
/// </summary>
|
|||
/// <param name="samples">The first element in the plane storage.</param>
|
|||
/// <param name="q0Offset">The offset of the first Q-side sample.</param>
|
|||
/// <param name="stride">The number of samples between adjacent rows.</param>
|
|||
/// <param name="distance">The signed sample distance from Q0.</param>
|
|||
/// <param name="value">The widened samples ordered along the edge.</param>
|
|||
public static abstract void StoreVector(ref TSample samples, int q0Offset, int stride, int distance, Vector128<int> value); |
|||
|
|||
/// <summary>
|
|||
/// Loads one sample at a signed distance across and an offset along the edge.
|
|||
/// </summary>
|
|||
/// <param name="samples">The first element in the plane storage.</param>
|
|||
/// <param name="q0Offset">The offset of the first Q-side sample.</param>
|
|||
/// <param name="stride">The number of samples between adjacent rows.</param>
|
|||
/// <param name="distance">The signed sample distance from Q0.</param>
|
|||
/// <param name="index">The sample offset along the edge.</param>
|
|||
/// <returns>The selected sample.</returns>
|
|||
public static abstract int LoadScalar(ref TSample samples, int q0Offset, int stride, int distance, int index); |
|||
|
|||
/// <summary>
|
|||
/// Stores one sample at a signed distance across and an offset along the edge.
|
|||
/// </summary>
|
|||
/// <param name="samples">The first element in the plane storage.</param>
|
|||
/// <param name="q0Offset">The offset of the first Q-side sample.</param>
|
|||
/// <param name="stride">The number of samples between adjacent rows.</param>
|
|||
/// <param name="distance">The signed sample distance from Q0.</param>
|
|||
/// <param name="index">The sample offset along the edge.</param>
|
|||
/// <param name="value">The filtered sample.</param>
|
|||
public static abstract void StoreScalar(ref TSample samples, int q0Offset, int stride, int distance, int index, int value); |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter; |
|||
|
|||
internal static partial class Av1DeblockingFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Accesses four rows across a vertical edge in eight-bit storage.
|
|||
/// </summary>
|
|||
private readonly struct VerticalByteEdgeOperator : IEdgeOperator<byte> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector128<int> LoadVector(ref byte samples, int q0Offset, int stride, int distance) |
|||
=> Vector128.Create( |
|||
(int)Unsafe.Add(ref samples, q0Offset + distance), |
|||
Unsafe.Add(ref samples, q0Offset + stride + distance), |
|||
Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance), |
|||
Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance)); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreVector(ref byte samples, int q0Offset, int stride, int distance, Vector128<int> value) |
|||
{ |
|||
Unsafe.Add(ref samples, q0Offset + distance) = (byte)value.GetElement(0); |
|||
Unsafe.Add(ref samples, q0Offset + stride + distance) = (byte)value.GetElement(1); |
|||
Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance) = (byte)value.GetElement(2); |
|||
Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance) = (byte)value.GetElement(3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int LoadScalar(ref byte samples, int q0Offset, int stride, int distance, int index) |
|||
=> Unsafe.Add(ref samples, q0Offset + (index * stride) + distance); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreScalar(ref byte samples, int q0Offset, int stride, int distance, int index, int value) |
|||
=> Unsafe.Add(ref samples, q0Offset + (index * stride) + distance) = (byte)value; |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter; |
|||
|
|||
internal static partial class Av1DeblockingFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Accesses four rows across a vertical edge in 16-bit storage.
|
|||
/// </summary>
|
|||
private readonly struct VerticalUInt16EdgeOperator : IEdgeOperator<ushort> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector128<int> LoadVector(ref ushort samples, int q0Offset, int stride, int distance) |
|||
=> Vector128.Create( |
|||
(int)Unsafe.Add(ref samples, q0Offset + distance), |
|||
Unsafe.Add(ref samples, q0Offset + stride + distance), |
|||
Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance), |
|||
Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance)); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreVector(ref ushort samples, int q0Offset, int stride, int distance, Vector128<int> value) |
|||
{ |
|||
Unsafe.Add(ref samples, q0Offset + distance) = (ushort)value.GetElement(0); |
|||
Unsafe.Add(ref samples, q0Offset + stride + distance) = (ushort)value.GetElement(1); |
|||
Unsafe.Add(ref samples, q0Offset + (2 * stride) + distance) = (ushort)value.GetElement(2); |
|||
Unsafe.Add(ref samples, q0Offset + (3 * stride) + distance) = (ushort)value.GetElement(3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int LoadScalar(ref ushort samples, int q0Offset, int stride, int distance, int index) |
|||
=> Unsafe.Add(ref samples, q0Offset + (index * stride) + distance); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreScalar(ref ushort samples, int q0Offset, int stride, int distance, int index, int value) |
|||
=> Unsafe.Add(ref samples, q0Offset + (index * stride) + distance) = (ushort)value; |
|||
} |
|||
} |
|||
@ -1,94 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Common.Helpers; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <summary>
|
|||
/// Reconstructs 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)); |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines eight-bit inverse-transform reconstruction arithmetic.
|
|||
/// </content>
|
|||
internal partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Reconstructs eight-bit samples from predicted values and inverse-transform residuals.
|
|||
/// </summary>
|
|||
internal readonly struct ByteOutputOperator : IAv1InverseTransformOutputOperator<byte> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static byte Add(byte prediction, int residual, int bitDepth) |
|||
{ |
|||
_ = bitDepth; |
|||
return (byte)Math.Clamp(prediction + residual, byte.MinValue, byte.MaxValue); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Add(ref byte prediction, ref byte destination, Vector128<int> residual, int bitDepth) |
|||
{ |
|||
_ = bitDepth; |
|||
|
|||
// 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.
|
|||
uint packed = Unsafe.ReadUnaligned<uint>(ref prediction); |
|||
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte()); |
|||
Vector128<int> predicted32 = Vector128.WidenLower(predicted16).AsInt32(); |
|||
Vector128<int> reconstructed = Vector128.Clamp(predicted32 + residual, Vector128<int>.Zero, Vector128.Create((int)byte.MaxValue)); |
|||
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.AsUInt32(), Vector128<uint>.Zero); |
|||
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero); |
|||
Unsafe.WriteUnaligned(ref destination, reconstructed8.AsUInt32().ToScalar()); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Add(ref byte prediction, ref byte destination, Vector256<int> residual, int bitDepth) |
|||
{ |
|||
_ = bitDepth; |
|||
|
|||
// 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.
|
|||
ulong packed = Unsafe.ReadUnaligned<ulong>(ref prediction); |
|||
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte()); |
|||
Vector256<int> predicted32 = Vector256.Create(Vector128.WidenLower(predicted16), Vector128.WidenUpper(predicted16)).AsInt32(); |
|||
Vector256<int> reconstructed = Vector256.Clamp(predicted32 + residual, Vector256<int>.Zero, Vector256.Create((int)byte.MaxValue)); |
|||
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.GetLower().AsUInt32(), reconstructed.GetUpper().AsUInt32()); |
|||
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero); |
|||
Unsafe.WriteUnaligned(ref destination, reconstructed8.AsUInt64().ToScalar()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
// 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>
|
|||
/// Defines high-bit-depth inverse-transform reconstruction arithmetic.
|
|||
/// </content>
|
|||
internal partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Reconstructs high-bit-depth samples from predicted values and inverse-transform residuals.
|
|||
/// </summary>
|
|||
internal readonly struct HighBitDepthOutputOperator : IAv1InverseTransformOutputOperator<short> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static short Add(short prediction, int residual, int bitDepth) |
|||
=> (short)Math.Clamp(prediction + residual, 0, (1 << bitDepth) - 1); |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Add(ref short prediction, ref short destination, Vector128<int> residual, int bitDepth) |
|||
{ |
|||
ulong packed = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<short, byte>(ref prediction)); |
|||
Vector128<int> predicted = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsInt16()); |
|||
Vector128<int> reconstructed = |
|||
Vector128.Clamp(predicted + residual, Vector128<int>.Zero, Vector128.Create((1 << bitDepth) - 1)); |
|||
|
|||
Vector128<short> narrowed = Vector128.Narrow(reconstructed, Vector128<int>.Zero); |
|||
Unsafe.WriteUnaligned(ref Unsafe.As<short, byte>(ref destination), narrowed.AsUInt64().ToScalar()); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Add(ref short prediction, ref short destination, Vector256<int> residual, int bitDepth) |
|||
{ |
|||
Vector256<int> predicted = Vector256_.Widen(Vector128.LoadUnsafe(ref prediction)); |
|||
Vector256<int> reconstructed = |
|||
Vector256.Clamp(predicted + residual, Vector256<int>.Zero, Vector256.Create((1 << bitDepth) - 1)); |
|||
|
|||
Vector128<short> narrowed = Vector128.Narrow(reconstructed.GetLower(), reconstructed.GetUpper()); |
|||
narrowed.StoreUnsafe(ref destination); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the inverse-transform reconstruction operator contract.
|
|||
/// </content>
|
|||
internal partial class Av1InverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines how inverse-transform residuals are added to decoded samples.
|
|||
/// </summary>
|
|||
/// <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 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 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); |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,314 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <content>
|
|||
/// Implements the forward asymmetric discrete sine transform stage networks.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformOperations |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the fixed eight-point ADST coefficient permutation.
|
|||
/// </summary>
|
|||
private static ReadOnlySpan<byte> Adst8OutputOrder => [1, 6, 3, 4, 5, 2, 7, 0]; |
|||
|
|||
/// <summary>
|
|||
/// Gets the first cosine index for each final sixteen-point ADST rotation.
|
|||
/// </summary>
|
|||
private static ReadOnlySpan<byte> Adst16FinalWeights => [2, 10, 18, 26, 34, 42, 50, 58]; |
|||
|
|||
/// <summary>
|
|||
/// Gets the fixed sixteen-point ADST coefficient permutation.
|
|||
/// </summary>
|
|||
private static ReadOnlySpan<byte> Adst16OutputOrder => [1, 14, 3, 12, 5, 10, 7, 8, 9, 6, 11, 4, 13, 2, 15, 0]; |
|||
|
|||
/// <summary>
|
|||
/// Applies the four-point forward asymmetric discrete sine transform to every independent 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 unused first transform-stage buffer.</param>
|
|||
/// <param name="buffer1">The unused second transform-stage buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
|
|||
public static void Adst4<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
|
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<TValue>.CreateRounding(cosBit); |
|||
TValue input0 = Load<TValue>(ref values, inputStride, 0); |
|||
TValue input1 = Load<TValue>(ref values, inputStride, 1); |
|||
TValue input2 = Load<TValue>(ref values, inputStride, 2); |
|||
TValue input3 = Load<TValue>(ref values, inputStride, 3); |
|||
TValue input01 = Av1ForwardTransformArithmetic<TValue>.Add(input0, input1); |
|||
|
|||
// Packed lanes form input0 + input1 before widening, matching Highway's observable saturating arithmetic.
|
|||
TValue output0 = Av1ForwardTransformArithmetic<TValue>.MultiplyAddRound( |
|||
sinpi[1], input0, sinpi[2], input1, sinpi[3], input2, sinpi[4], input3, cosBit, in rounding); |
|||
|
|||
TValue output1 = Av1ForwardTransformArithmetic<TValue>.MultiplyAddRound( |
|||
sinpi[3], input01, -sinpi[3], input3, 0, input0, 0, input0, cosBit, in rounding); |
|||
|
|||
TValue output2 = Av1ForwardTransformArithmetic<TValue>.MultiplyAddRound( |
|||
sinpi[4], input0, -sinpi[1], input1, -sinpi[3], input2, sinpi[2], input3, cosBit, in rounding); |
|||
|
|||
// This expression preserves Highway's widened w2 - w0 + 3 * v5 sequence with one rounding point.
|
|||
TValue output3 = Av1ForwardTransformArithmetic<TValue>.MultiplyAddRound( |
|||
sinpi[4] - sinpi[1], |
|||
input0, |
|||
-sinpi[1] - sinpi[2], |
|||
input1, |
|||
sinpi[3], |
|||
input2, |
|||
sinpi[2] - sinpi[4], |
|||
input3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the eight-point forward asymmetric discrete sine transform to every independent 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 void Adst8<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<TValue>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 applies the ADST permutation and signs while the source block is still read-only.
|
|||
buffer0[0] = Load<TValue>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 7)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 3)); |
|||
buffer0[3] = Load<TValue>(ref values, inputStride, 4); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 1)); |
|||
buffer0[5] = Load<TValue>(ref values, inputStride, 6); |
|||
buffer0[6] = Load<TValue>(ref values, inputStride, 2); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 5)); |
|||
|
|||
// Stage 2 rotates the second pair in each four-value group while copying the already aligned pairs.
|
|||
buffer1[0] = buffer0[0]; |
|||
buffer1[1] = buffer0[1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[2], buffer0[3], ref buffer1, 2, 3, cosBit, in rounding); |
|||
buffer1[4] = buffer0[4]; |
|||
buffer1[5] = buffer0[5]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[6], buffer0[7], ref buffer1, 6, 7, cosBit, in rounding); |
|||
|
|||
// Stage 3 combines the rotated and copied pairs into two independent four-value groups.
|
|||
for (int group = 0; group < 8; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper group by pi/8 while the completed lower group passes through unchanged.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[4] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[16], buffer0[4], cospi[48], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[48], buffer0[4], -cospi[16], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(-cospi[48], buffer0[6], cospi[16], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[16], buffer0[6], cospi[48], buffer0[7], cosBit, in rounding); |
|||
|
|||
// Stage 5 creates the four final butterfly pairs spanning the two groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[i], buffer1[i + 4], out buffer0[i], out buffer0[i + 4]); |
|||
} |
|||
|
|||
// Stage 6 applies the remaining odd-angle rotations.
|
|||
buffer1[0] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[4], buffer0[0], cospi[60], buffer0[1], cosBit, in rounding); |
|||
buffer1[1] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[60], buffer0[0], -cospi[4], buffer0[1], cosBit, in rounding); |
|||
buffer1[2] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[20], buffer0[2], cospi[44], buffer0[3], cosBit, in rounding); |
|||
buffer1[3] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[44], buffer0[2], -cospi[20], buffer0[3], cosBit, in rounding); |
|||
buffer1[4] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[36], buffer0[4], cospi[28], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[28], buffer0[4], -cospi[36], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[52], buffer0[6], cospi[12], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[12], buffer0[6], -cospi[52], buffer0[7], cosBit, in rounding); |
|||
|
|||
ReadOnlySpan<byte> outputOrder = Adst8OutputOrder; |
|||
|
|||
// Stage 7 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Applies the sixteen-point forward asymmetric discrete sine transform to every independent 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 void Adst16<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<TValue>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 is the bit-reversed ADST input order with the normative alternating signs.
|
|||
buffer0[0] = Load<TValue>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 15)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 7)); |
|||
buffer0[3] = Load<TValue>(ref values, inputStride, 8); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 3)); |
|||
buffer0[5] = Load<TValue>(ref values, inputStride, 12); |
|||
buffer0[6] = Load<TValue>(ref values, inputStride, 4); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 11)); |
|||
buffer0[8] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 1)); |
|||
buffer0[9] = Load<TValue>(ref values, inputStride, 14); |
|||
buffer0[10] = Load<TValue>(ref values, inputStride, 6); |
|||
buffer0[11] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 9)); |
|||
buffer0[12] = Load<TValue>(ref values, inputStride, 2); |
|||
buffer0[13] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 13)); |
|||
buffer0[14] = Av1ForwardTransformArithmetic<TValue>.Negate(Load<TValue>(ref values, inputStride, 5)); |
|||
buffer0[15] = Load<TValue>(ref values, inputStride, 10); |
|||
|
|||
// Stage 2 rotates the second pair in each group of four while copying the first pair unchanged.
|
|||
for (int group = 0; group < 16; group += 4) |
|||
{ |
|||
buffer1[group] = buffer0[group]; |
|||
buffer1[group + 1] = buffer0[group + 1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[group + 2], buffer0[group + 3], ref buffer1, group + 2, group + 3, cosBit, in rounding); |
|||
} |
|||
|
|||
// Stage 3 combines adjacent pairs within each group of four.
|
|||
for (int group = 0; group < 16; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper pair of each eight-value group by pi/8.
|
|||
for (int group = 0; group < 16; group += 8) |
|||
{ |
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[group + i] = buffer0[group + i]; |
|||
} |
|||
|
|||
buffer1[group + 4] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly( |
|||
cospi[16], buffer0[group + 4], cospi[48], buffer0[group + 5], cosBit, in rounding); |
|||
|
|||
buffer1[group + 5] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly( |
|||
cospi[48], buffer0[group + 4], -cospi[16], buffer0[group + 5], cosBit, in rounding); |
|||
|
|||
buffer1[group + 6] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly( |
|||
-cospi[48], buffer0[group + 6], cospi[16], buffer0[group + 7], cosBit, in rounding); |
|||
|
|||
buffer1[group + 7] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly( |
|||
cospi[16], buffer0[group + 6], cospi[48], buffer0[group + 7], cosBit, in rounding); |
|||
} |
|||
|
|||
// Stage 5 combines the lower and upper quartets within each eight-value group.
|
|||
for (int group = 0; group < 16; group += 8) |
|||
{ |
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 4], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 4]); |
|||
} |
|||
} |
|||
|
|||
// Stage 6 rotates the upper octet by pi/16 while retaining the completed lower octet.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[8] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[8], buffer0[8], cospi[56], buffer0[9], cosBit, in rounding); |
|||
buffer1[9] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[56], buffer0[8], -cospi[8], buffer0[9], cosBit, in rounding); |
|||
buffer1[10] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[40], buffer0[10], cospi[24], buffer0[11], cosBit, in rounding); |
|||
buffer1[11] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[24], buffer0[10], -cospi[40], buffer0[11], cosBit, in rounding); |
|||
buffer1[12] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(-cospi[56], buffer0[12], cospi[8], buffer0[13], cosBit, in rounding); |
|||
buffer1[13] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[8], buffer0[12], cospi[56], buffer0[13], cosBit, in rounding); |
|||
buffer1[14] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(-cospi[24], buffer0[14], cospi[40], buffer0[15], cosBit, in rounding); |
|||
buffer1[15] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly(cospi[40], buffer0[14], cospi[24], buffer0[15], cosBit, in rounding); |
|||
|
|||
// Stage 7 creates the eight final butterfly pairs spanning both octets.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[i], buffer1[i + 8], out buffer0[i], out buffer0[i + 8]); |
|||
} |
|||
|
|||
ReadOnlySpan<byte> finalWeights = Adst16FinalWeights; |
|||
|
|||
// Stage 8 applies the final odd-angle rotations. The compact weight table preserves their normative order
|
|||
// without allocating a per-call array or duplicating the complementary cosine-index calculation.
|
|||
for (int pair = 0; pair < 8; pair++) |
|||
{ |
|||
int first = finalWeights[pair]; |
|||
int second = 64 - first; |
|||
int index = pair * 2; |
|||
buffer1[index] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly( |
|||
cospi[first], buffer0[index], cospi[second], buffer0[index + 1], cosBit, in rounding); |
|||
|
|||
buffer1[index + 1] = Av1ForwardTransformArithmetic<TValue>.HalfButterfly( |
|||
cospi[second], buffer0[index], -cospi[first], buffer0[index + 1], cosBit, in rounding); |
|||
} |
|||
|
|||
ReadOnlySpan<byte> outputOrder = Adst16OutputOrder; |
|||
|
|||
// Stage 9 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
} |
|||
@ -1,218 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <content>
|
|||
/// Implements the sixteen-point forward DCT stage network.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformOperations |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the sixteen-point forward discrete cosine transform to every independent 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 void Dct16<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<TValue>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 forms the mirror-symmetric pairs consumed by the recursive even and odd factorizations.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
Load<TValue>(ref values, inputStride, i), |
|||
Load<TValue>(ref values, inputStride, 15 - i), |
|||
out buffer0[i], |
|||
out buffer0[15 - i]); |
|||
} |
|||
|
|||
// Stage 2 begins the recursive factorization of the even half and rotates the central odd pairs by pi/4.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[i], buffer0[7 - i], out buffer1[i], out buffer1[7 - i]); |
|||
} |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[10], |
|||
buffer0[13], |
|||
out buffer1[10], |
|||
out buffer1[13], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[11], |
|||
buffer0[12], |
|||
out buffer1[11], |
|||
out buffer1[12], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 reduces both eight-value groups into the four-value units consumed by the terminal rotations.
|
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[i], buffer1[3 - i], out buffer0[i], out buffer0[3 - i]); |
|||
} |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer1[5], |
|||
buffer1[6], |
|||
out buffer0[5], |
|||
out buffer0[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[8 + i], buffer1[11 - i], out buffer0[8 + i], out buffer0[11 - i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[15 - i], buffer1[12 + i], out buffer0[15 - i], out buffer0[12 + i]); |
|||
} |
|||
|
|||
// The even coefficients become final at stages 4 and 5, so they are written directly to their AV1 order.
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer0[0], |
|||
buffer0[1], |
|||
out TValue output0, |
|||
out TValue output8, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer0[3], |
|||
buffer0[2], |
|||
out TValue output4, |
|||
out TValue output12, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[4], buffer0[5], out buffer1[4], out buffer1[5]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[7], buffer0[6], out buffer1[7], out buffer1[6]); |
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[16], |
|||
cospi[48], |
|||
buffer0[9], |
|||
buffer0[14], |
|||
out buffer1[9], |
|||
out buffer1[14], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[48], |
|||
-cospi[16], |
|||
buffer0[10], |
|||
buffer0[13], |
|||
out buffer1[10], |
|||
out buffer1[13], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer1[7], |
|||
buffer1[4], |
|||
out TValue output2, |
|||
out TValue output14, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer1[6], |
|||
buffer1[5], |
|||
out TValue output10, |
|||
out TValue output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[8], buffer1[9], out buffer0[8], out buffer0[9]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[11], buffer1[10], out buffer0[11], out buffer0[10]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[12], buffer1[13], out buffer0[12], out buffer0[13]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[15], buffer1[14], out buffer0[15], out buffer0[14]); |
|||
|
|||
// Stage 6 applies the final pi/32 odd-frequency rotations. The following stores perform only the normative
|
|||
// coefficient permutation, so each rotation result is named by its final destination.
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[4], |
|||
cospi[60], |
|||
buffer0[15], |
|||
buffer0[8], |
|||
out TValue output1, |
|||
out TValue output15, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[36], |
|||
cospi[28], |
|||
buffer0[14], |
|||
buffer0[9], |
|||
out TValue output9, |
|||
out TValue output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[20], |
|||
cospi[44], |
|||
buffer0[13], |
|||
buffer0[10], |
|||
out TValue output5, |
|||
out TValue output11, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[52], |
|||
cospi[12], |
|||
buffer0[12], |
|||
buffer0[11], |
|||
out TValue output13, |
|||
out TValue output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
Store(ref values, outputStride, 8, output8); |
|||
Store(ref values, outputStride, 9, output9); |
|||
Store(ref values, outputStride, 10, output10); |
|||
Store(ref values, outputStride, 11, output11); |
|||
Store(ref values, outputStride, 12, output12); |
|||
Store(ref values, outputStride, 13, output13); |
|||
Store(ref values, outputStride, 14, output14); |
|||
Store(ref values, outputStride, 15, output15); |
|||
} |
|||
} |
|||
@ -1,263 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <content>
|
|||
/// Implements the thirty-two-point forward DCT stage network.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformOperations |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the thirty-two-point forward discrete cosine transform to every independent 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 void Dct32<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<TValue>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 consumes the source block completely before any final coefficient is stored back into it.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
Load<TValue>(ref values, inputStride, i), |
|||
Load<TValue>(ref values, inputStride, 31 - i), |
|||
out buffer1[i], |
|||
out buffer1[31 - i]); |
|||
} |
|||
|
|||
// Stage 2 starts the recursive radix-2 factorization and rotates the central odd-frequency pairs.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[i], buffer1[15 - i], out buffer0[i], out buffer0[15 - i]); |
|||
} |
|||
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer1[20 + i], |
|||
buffer1[27 - i], |
|||
out buffer0[20 + i], |
|||
out buffer0[27 - i], |
|||
cosBit, |
|||
in rounding); |
|||
} |
|||
|
|||
// Stage 3 reduces the even half and folds the next odd-frequency groups into paired sums and differences.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[i], buffer0[7 - i], out buffer1[i], out buffer1[7 - i]); |
|||
} |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[10], |
|||
buffer0[13], |
|||
out buffer1[10], |
|||
out buffer1[13], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[11], |
|||
buffer0[12], |
|||
out buffer1[11], |
|||
out buffer1[12], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[16 + i], buffer0[23 - i], out buffer1[16 + i], out buffer1[23 - i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[31 - i], buffer0[24 + i], out buffer1[31 - i], out buffer1[24 + i]); |
|||
} |
|||
|
|||
// Stage 4 continues the factorization as independent eight-value groups.
|
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[i], buffer1[3 - i], out buffer0[i], out buffer0[3 - i]); |
|||
} |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer1[5], |
|||
buffer1[6], |
|||
out buffer0[5], |
|||
out buffer0[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[8 + i], buffer1[11 - i], out buffer0[8 + i], out buffer0[11 - i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[15 - i], buffer1[12 + i], out buffer0[15 - i], out buffer0[12 + i]); |
|||
} |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[16], |
|||
cospi[48], |
|||
buffer1[18], |
|||
buffer1[29], |
|||
out buffer0[18], |
|||
out buffer0[29], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[16], |
|||
cospi[48], |
|||
buffer1[19], |
|||
buffer1[28], |
|||
out buffer0[19], |
|||
out buffer0[28], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[48], |
|||
-cospi[16], |
|||
buffer1[20], |
|||
buffer1[27], |
|||
out buffer0[20], |
|||
out buffer0[27], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[48], |
|||
-cospi[16], |
|||
buffer1[21], |
|||
buffer1[26], |
|||
out buffer0[21], |
|||
out buffer0[26], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 5 completes the low-frequency DCT and rotates the first separated odd groups. Final coefficients are
|
|||
// retired directly to the block instead of being copied through a third workspace.
|
|||
ButterflyStore(cospi[32], cospi[32], buffer0[0], buffer0[1], ref values, outputStride, 0, 16, cosBit, in rounding); |
|||
ButterflyStore(cospi[16], cospi[48], buffer0[3], buffer0[2], ref values, outputStride, 8, 24, cosBit, in rounding); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[4], buffer0[5], out buffer1[4], out buffer1[5]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[7], buffer0[6], out buffer1[7], out buffer1[6]); |
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[16], |
|||
cospi[48], |
|||
buffer0[9], |
|||
buffer0[14], |
|||
out buffer1[9], |
|||
out buffer1[14], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[48], |
|||
-cospi[16], |
|||
buffer0[10], |
|||
buffer0[13], |
|||
out buffer1[10], |
|||
out buffer1[13], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[16], buffer0[19], out buffer1[16], out buffer1[19]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[17], buffer0[18], out buffer1[17], out buffer1[18]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[23], buffer0[20], out buffer1[23], out buffer1[20]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[22], buffer0[21], out buffer1[22], out buffer1[21]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[24], buffer0[27], out buffer1[24], out buffer1[27]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[25], buffer0[26], out buffer1[25], out buffer1[26]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[31], buffer0[28], out buffer1[31], out buffer1[28]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[30], buffer0[29], out buffer1[30], out buffer1[29]); |
|||
|
|||
// Stage 6 merges adjacent odd-frequency terms with the sign pattern required by the next rotations.
|
|||
ButterflyStore(cospi[8], cospi[56], buffer1[7], buffer1[4], ref values, outputStride, 4, 28, cosBit, in rounding); |
|||
ButterflyStore(cospi[40], cospi[24], buffer1[6], buffer1[5], ref values, outputStride, 20, 12, cosBit, in rounding); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[8], buffer1[9], out buffer0[8], out buffer0[9]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[11], buffer1[10], out buffer0[11], out buffer0[10]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[12], buffer1[13], out buffer0[12], out buffer0[13]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[15], buffer1[14], out buffer0[15], out buffer0[14]); |
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[8], |
|||
cospi[56], |
|||
buffer1[17], |
|||
buffer1[30], |
|||
out buffer0[17], |
|||
out buffer0[30], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[56], |
|||
-cospi[8], |
|||
buffer1[18], |
|||
buffer1[29], |
|||
out buffer0[18], |
|||
out buffer0[29], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[40], |
|||
cospi[24], |
|||
buffer1[21], |
|||
buffer1[26], |
|||
out buffer0[21], |
|||
out buffer0[26], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[24], |
|||
-cospi[40], |
|||
buffer1[22], |
|||
buffer1[25], |
|||
out buffer0[22], |
|||
out buffer0[25], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 7 applies the pi/32 rotations to the next odd-frequency level.
|
|||
ButterflyStore(cospi[4], cospi[60], buffer0[15], buffer0[8], ref values, outputStride, 2, 30, cosBit, in rounding); |
|||
ButterflyStore(cospi[36], cospi[28], buffer0[14], buffer0[9], ref values, outputStride, 18, 14, cosBit, in rounding); |
|||
ButterflyStore(cospi[20], cospi[44], buffer0[13], buffer0[10], ref values, outputStride, 10, 22, cosBit, in rounding); |
|||
ButterflyStore(cospi[52], cospi[12], buffer0[12], buffer0[11], ref values, outputStride, 26, 6, cosBit, in rounding); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[16], buffer0[17], out buffer1[16], out buffer1[17]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[19], buffer0[18], out buffer1[19], out buffer1[18]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[20], buffer0[21], out buffer1[20], out buffer1[21]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[23], buffer0[22], out buffer1[23], out buffer1[22]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[24], buffer0[25], out buffer1[24], out buffer1[25]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[27], buffer0[26], out buffer1[27], out buffer1[26]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[28], buffer0[29], out buffer1[28], out buffer1[29]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[31], buffer0[30], out buffer1[31], out buffer1[30]); |
|||
|
|||
// Stages 8 and 9 fuse the terminal pi/64 rotations with the output permutation because none of their results
|
|||
// are consumed by another arithmetic stage.
|
|||
ButterflyStore(cospi[2], cospi[62], buffer1[31], buffer1[16], ref values, outputStride, 1, 31, cosBit, in rounding); |
|||
ButterflyStore(cospi[34], cospi[30], buffer1[30], buffer1[17], ref values, outputStride, 17, 15, cosBit, in rounding); |
|||
ButterflyStore(cospi[18], cospi[46], buffer1[29], buffer1[18], ref values, outputStride, 9, 23, cosBit, in rounding); |
|||
ButterflyStore(cospi[50], cospi[14], buffer1[28], buffer1[19], ref values, outputStride, 25, 7, cosBit, in rounding); |
|||
ButterflyStore(cospi[10], cospi[54], buffer1[27], buffer1[20], ref values, outputStride, 5, 27, cosBit, in rounding); |
|||
ButterflyStore(cospi[42], cospi[22], buffer1[26], buffer1[21], ref values, outputStride, 21, 11, cosBit, in rounding); |
|||
ButterflyStore(cospi[26], cospi[38], buffer1[25], buffer1[22], ref values, outputStride, 13, 19, cosBit, in rounding); |
|||
ButterflyStore(cospi[58], cospi[6], buffer1[24], buffer1[23], ref values, outputStride, 29, 3, cosBit, in rounding); |
|||
} |
|||
} |
|||
@ -1,285 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <content>
|
|||
/// Implements the sixty-four-point forward DCT stage network.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformOperations |
|||
{ |
|||
/// <summary>
|
|||
/// Identifies coefficient positions whose final value resides in the first stage buffer.
|
|||
/// </summary>
|
|||
private const ulong Dct64Buffer0OutputMask = |
|||
(1UL << 2) | (1UL << 6) | (1UL << 8) | (1UL << 10) | (1UL << 14) | |
|||
(1UL << 18) | (1UL << 22) | (1UL << 24) | (1UL << 26) | (1UL << 30) | |
|||
(1UL << 34) | (1UL << 38) | (1UL << 40) | (1UL << 42) | (1UL << 46) | |
|||
(1UL << 50) | (1UL << 54) | (1UL << 56) | (1UL << 58) | (1UL << 62); |
|||
|
|||
/// <summary>
|
|||
/// Gets the stage-nine rotation order for the middle quarter of the sixty-four-point DCT.
|
|||
/// </summary>
|
|||
private static ReadOnlySpan<byte> Dct64Stage9RotationOrder => [2, 34, 18, 50, 10, 42, 26, 58]; |
|||
|
|||
/// <summary>
|
|||
/// Gets the stage-ten rotation order for the upper half of the sixty-four-point DCT.
|
|||
/// </summary>
|
|||
private static ReadOnlySpan<byte> Dct64Stage10RotationOrder => [1, 33, 17, 49, 9, 41, 25, 57, 5, 37, 21, 53, 13, 45, 29, 61]; |
|||
|
|||
/// <summary>
|
|||
/// Gets the mapping from coefficient order to the final staged value.
|
|||
/// </summary>
|
|||
private static ReadOnlySpan<byte> Dct64OutputOrder => |
|||
[ |
|||
0, 32, 16, 48, 8, 40, 24, 56, 4, 36, 20, 52, 12, 44, 28, 60, |
|||
2, 34, 18, 50, 10, 42, 26, 58, 6, 38, 22, 54, 14, 46, 30, 62, |
|||
1, 33, 17, 49, 9, 41, 25, 57, 5, 37, 21, 53, 13, 45, 29, 61, |
|||
3, 35, 19, 51, 11, 43, 27, 59, 7, 39, 23, 55, 15, 47, 31, 63, |
|||
]; |
|||
|
|||
/// <summary>
|
|||
/// Applies the sixty-four-point forward discrete cosine transform to every independent 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 void Dct64<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<TValue>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 consumes every spatial value before the strided block becomes available for final coefficients.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
Load<TValue>(ref values, inputStride, i), |
|||
Load<TValue>(ref values, inputStride, 63 - i), |
|||
out buffer0[i], |
|||
out buffer0[63 - i]); |
|||
} |
|||
|
|||
// Stage 2 begins the recursive radix-2 factorization and rotates the central odd-frequency pairs.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[i], buffer0[31 - i], out buffer1[i], out buffer1[31 - i]); |
|||
} |
|||
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Butterfly(-cospi[32], cospi[32], buffer0[40 + i], buffer0[55 - i], ref buffer1, 40 + i, 55 - i, cosBit, in rounding); |
|||
} |
|||
|
|||
// Stage 3 reduces the even half and folds the next odd-frequency groups into paired sums and differences.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[i], buffer1[15 - i], out buffer0[i], out buffer0[15 - i]); |
|||
} |
|||
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Butterfly(-cospi[32], cospi[32], buffer1[20 + i], buffer1[27 - i], ref buffer0, 20 + i, 27 - i, cosBit, in rounding); |
|||
} |
|||
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[32 + i], buffer1[47 - i], out buffer0[32 + i], out buffer0[47 - i]); |
|||
} |
|||
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[63 - i], buffer1[48 + i], out buffer0[63 - i], out buffer0[48 + i]); |
|||
} |
|||
|
|||
// Stage 4 continues the factorization as independent sixteen-value groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[i], buffer0[7 - i], out buffer1[i], out buffer1[7 - i]); |
|||
} |
|||
|
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Butterfly(-cospi[32], cospi[32], buffer0[10 + i], buffer0[13 - i], ref buffer1, 10 + i, 13 - i, cosBit, in rounding); |
|||
} |
|||
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[16 + i], buffer0[23 - i], out buffer1[16 + i], out buffer1[23 - i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[31 - i], buffer0[24 + i], out buffer1[31 - i], out buffer1[24 + i]); |
|||
Butterfly(-cospi[16], cospi[48], buffer0[36 + i], buffer0[59 - i], ref buffer1, 36 + i, 59 - i, cosBit, in rounding); |
|||
} |
|||
|
|||
for (int i = 4; i < 8; i++) |
|||
{ |
|||
Butterfly(-cospi[48], -cospi[16], buffer0[36 + i], buffer0[59 - i], ref buffer1, 36 + i, 59 - i, cosBit, in rounding); |
|||
} |
|||
|
|||
// Stage 5 reduces the sixteen-value groups into the eight-value DCT and ADST building blocks.
|
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[i], buffer1[3 - i], out buffer0[i], out buffer0[3 - i]); |
|||
} |
|||
|
|||
Butterfly(-cospi[32], cospi[32], buffer1[5], buffer1[6], ref buffer0, 5, 6, cosBit, in rounding); |
|||
|
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[8 + i], buffer1[11 - i], out buffer0[8 + i], out buffer0[11 - i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[15 - i], buffer1[12 + i], out buffer0[15 - i], out buffer0[12 + i]); |
|||
Butterfly(-cospi[16], cospi[48], buffer1[18 + i], buffer1[29 - i], ref buffer0, 18 + i, 29 - i, cosBit, in rounding); |
|||
} |
|||
|
|||
for (int i = 2; i < 4; i++) |
|||
{ |
|||
Butterfly(-cospi[48], -cospi[16], buffer1[18 + i], buffer1[29 - i], ref buffer0, 18 + i, 29 - i, cosBit, in rounding); |
|||
} |
|||
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[32 + i], buffer1[39 - i], out buffer0[32 + i], out buffer0[39 - i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[47 - i], buffer1[40 + i], out buffer0[47 - i], out buffer0[40 + i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[48 + i], buffer1[55 - i], out buffer0[48 + i], out buffer0[55 - i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[63 - i], buffer1[56 + i], out buffer0[63 - i], out buffer0[56 + i]); |
|||
} |
|||
|
|||
// Stage 6 completes the low-frequency DCT and rotates the first separated odd-frequency groups.
|
|||
Butterfly(cospi[32], cospi[32], buffer0[0], buffer0[1], ref buffer1, 0, 1, cosBit, in rounding); |
|||
Butterfly(cospi[16], cospi[48], buffer0[3], buffer0[2], ref buffer1, 2, 3, cosBit, in rounding); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[4], buffer0[5], out buffer1[4], out buffer1[5]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[7], buffer0[6], out buffer1[7], out buffer1[6]); |
|||
Butterfly(-cospi[16], cospi[48], buffer0[9], buffer0[14], ref buffer1, 9, 14, cosBit, in rounding); |
|||
Butterfly(-cospi[48], -cospi[16], buffer0[10], buffer0[13], ref buffer1, 10, 13, cosBit, in rounding); |
|||
|
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[16 + i], buffer0[19 - i], out buffer1[16 + i], out buffer1[19 - i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[23 - i], buffer0[20 + i], out buffer1[23 - i], out buffer1[20 + i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[24 + i], buffer0[27 - i], out buffer1[24 + i], out buffer1[27 - i]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[31 - i], buffer0[28 + i], out buffer1[31 - i], out buffer1[28 + i]); |
|||
Butterfly(-cospi[8], cospi[56], buffer0[34 + i], buffer0[61 - i], ref buffer1, 34 + i, 61 - i, cosBit, in rounding); |
|||
Butterfly(-cospi[40], cospi[24], buffer0[42 + i], buffer0[53 - i], ref buffer1, 42 + i, 53 - i, cosBit, in rounding); |
|||
} |
|||
|
|||
for (int i = 2; i < 4; i++) |
|||
{ |
|||
Butterfly(-cospi[56], -cospi[8], buffer0[34 + i], buffer0[61 - i], ref buffer1, 34 + i, 61 - i, cosBit, in rounding); |
|||
Butterfly(-cospi[24], -cospi[40], buffer0[42 + i], buffer0[53 - i], ref buffer1, 42 + i, 53 - i, cosBit, in rounding); |
|||
} |
|||
|
|||
// Stage 7 merges adjacent odd-frequency terms with the sign pattern required by the next rotations.
|
|||
Butterfly(cospi[8], cospi[56], buffer1[7], buffer1[4], ref buffer0, 4, 7, cosBit, in rounding); |
|||
Butterfly(cospi[40], cospi[24], buffer1[6], buffer1[5], ref buffer0, 5, 6, cosBit, in rounding); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[8], buffer1[9], out buffer0[8], out buffer0[9]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[11], buffer1[10], out buffer0[11], out buffer0[10]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[12], buffer1[13], out buffer0[12], out buffer0[13]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[15], buffer1[14], out buffer0[15], out buffer0[14]); |
|||
Butterfly(-cospi[8], cospi[56], buffer1[17], buffer1[30], ref buffer0, 17, 30, cosBit, in rounding); |
|||
Butterfly(-cospi[56], -cospi[8], buffer1[18], buffer1[29], ref buffer0, 18, 29, cosBit, in rounding); |
|||
Butterfly(-cospi[40], cospi[24], buffer1[21], buffer1[26], ref buffer0, 21, 26, cosBit, in rounding); |
|||
Butterfly(-cospi[24], -cospi[40], buffer1[22], buffer1[25], ref buffer0, 22, 25, cosBit, in rounding); |
|||
|
|||
for (int group = 0; group < 4; group++) |
|||
{ |
|||
int offset = group * 8; |
|||
|
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
buffer0[32 + offset + i], |
|||
buffer1[35 + offset - i], |
|||
out buffer0[32 + offset + i], |
|||
out buffer0[35 + offset - i]); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
buffer0[39 + offset - i], |
|||
buffer1[36 + offset + i], |
|||
out buffer0[39 + offset - i], |
|||
out buffer0[36 + offset + i]); |
|||
} |
|||
} |
|||
|
|||
// Stage 8 applies the next level of odd-frequency rotations.
|
|||
Butterfly(cospi[4], cospi[60], buffer0[15], buffer0[8], ref buffer1, 8, 15, cosBit, in rounding); |
|||
Butterfly(cospi[36], cospi[28], buffer0[14], buffer0[9], ref buffer1, 9, 14, cosBit, in rounding); |
|||
Butterfly(cospi[20], cospi[44], buffer0[13], buffer0[10], ref buffer1, 10, 13, cosBit, in rounding); |
|||
Butterfly(cospi[52], cospi[12], buffer0[12], buffer0[11], ref buffer1, 11, 12, cosBit, in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[16], buffer0[17], out buffer1[16], out buffer1[17]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[19], buffer0[18], out buffer1[19], out buffer1[18]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[20], buffer0[21], out buffer1[20], out buffer1[21]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[23], buffer0[22], out buffer1[23], out buffer1[22]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[24], buffer0[25], out buffer1[24], out buffer1[25]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[27], buffer0[26], out buffer1[27], out buffer1[26]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[28], buffer0[29], out buffer1[28], out buffer1[29]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[31], buffer0[30], out buffer1[31], out buffer1[30]); |
|||
|
|||
Butterfly(-cospi[4], cospi[60], buffer0[33], buffer0[62], ref buffer1, 33, 62, cosBit, in rounding); |
|||
Butterfly(-cospi[60], -cospi[4], buffer0[34], buffer0[61], ref buffer1, 34, 61, cosBit, in rounding); |
|||
Butterfly(-cospi[36], cospi[28], buffer0[37], buffer0[58], ref buffer1, 37, 58, cosBit, in rounding); |
|||
Butterfly(-cospi[28], -cospi[36], buffer0[38], buffer0[57], ref buffer1, 38, 57, cosBit, in rounding); |
|||
Butterfly(-cospi[20], cospi[44], buffer0[41], buffer0[54], ref buffer1, 41, 54, cosBit, in rounding); |
|||
Butterfly(-cospi[44], -cospi[20], buffer0[42], buffer0[53], ref buffer1, 42, 53, cosBit, in rounding); |
|||
Butterfly(-cospi[52], cospi[12], buffer0[45], buffer0[50], ref buffer1, 45, 50, cosBit, in rounding); |
|||
Butterfly(-cospi[12], -cospi[52], buffer0[46], buffer0[49], ref buffer1, 46, 49, cosBit, in rounding); |
|||
|
|||
// Stage 9 merges the remaining odd-frequency pairs before their terminal rotations. The table preserves the
|
|||
// non-linear rotation order while keeping the constants in compile-time data.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
int low = 16 + i; |
|||
int high = 31 - i; |
|||
int odd = Dct64Stage9RotationOrder[i]; |
|||
Butterfly(cospi[odd], cospi[64 - odd], buffer1[high], buffer1[low], ref buffer0, low, high, cosBit, in rounding); |
|||
} |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[32], buffer1[33], out buffer0[32], out buffer0[33]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[35], buffer1[34], out buffer0[35], out buffer0[34]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[36], buffer1[37], out buffer0[36], out buffer0[37]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[39], buffer1[38], out buffer0[39], out buffer0[38]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[40], buffer1[41], out buffer0[40], out buffer0[41]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[43], buffer1[42], out buffer0[43], out buffer0[42]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[44], buffer1[45], out buffer0[44], out buffer0[45]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[47], buffer1[46], out buffer0[47], out buffer0[46]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[48], buffer1[49], out buffer0[48], out buffer0[49]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[51], buffer1[50], out buffer0[51], out buffer0[50]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[52], buffer1[53], out buffer0[52], out buffer0[53]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[55], buffer1[54], out buffer0[55], out buffer0[54]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[56], buffer1[57], out buffer0[56], out buffer0[57]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[59], buffer1[58], out buffer0[59], out buffer0[58]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[60], buffer1[61], out buffer0[60], out buffer0[61]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[63], buffer1[62], out buffer0[63], out buffer0[62]); |
|||
|
|||
// Stage 10 applies the pi/64 rotations to the penultimate odd-frequency level.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
int low = 32 + i; |
|||
int high = 63 - i; |
|||
int odd = Dct64Stage10RotationOrder[i]; |
|||
Butterfly(cospi[odd], cospi[64 - odd], buffer0[high], buffer0[low], ref buffer1, low, high, cosBit, in rounding); |
|||
} |
|||
|
|||
// Stage 11 applies the terminal permutation. The fused stages omit pass-through copies, so sources 4-7 and
|
|||
// 16-31 remain in buffer0 at retirement,
|
|||
// while every other source resides in buffer1. The mask maps that ownership through AV1 coefficient order.
|
|||
ReadOnlySpan<byte> outputOrder = Dct64OutputOrder; |
|||
|
|||
for (int i = 0; i < 64; i++) |
|||
{ |
|||
int sourceIndex = outputOrder[i]; |
|||
TValue value = ((Dct64Buffer0OutputMask >> i) & 1) != 0 ? buffer0[sourceIndex] : buffer1[sourceIndex]; |
|||
|
|||
Store(ref values, outputStride, i, value); |
|||
} |
|||
} |
|||
} |
|||
@ -1,125 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <content>
|
|||
/// Implements the eight-point forward DCT stage network.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformOperations |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the eight-point forward discrete cosine transform to every independent 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 void Dct8<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<TValue>.CreateRounding(cosBit); |
|||
|
|||
// Stages 1 and 2 split the even and odd terms. The asymmetric destinations mirror Highway's buffer
|
|||
// ownership, allowing the later even butterflies to write their final coefficients directly to the block.
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
Load<TValue>(ref values, inputStride, 0), |
|||
Load<TValue>(ref values, inputStride, 7), |
|||
out buffer0[0], |
|||
out buffer1[7]); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
Load<TValue>(ref values, inputStride, 1), |
|||
Load<TValue>(ref values, inputStride, 6), |
|||
out buffer0[1], |
|||
out buffer0[6]); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
Load<TValue>(ref values, inputStride, 2), |
|||
Load<TValue>(ref values, inputStride, 5), |
|||
out buffer0[2], |
|||
out buffer0[5]); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract( |
|||
Load<TValue>(ref values, inputStride, 3), |
|||
Load<TValue>(ref values, inputStride, 4), |
|||
out buffer0[3], |
|||
out buffer1[4]); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[0], buffer0[3], out buffer1[0], out buffer1[3]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer0[1], buffer0[2], out buffer1[1], out buffer1[2]); |
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[5], |
|||
buffer0[6], |
|||
out buffer1[5], |
|||
out buffer1[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 completes the even half directly in coefficient order and prepares the four remaining odd terms.
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer1[0], |
|||
buffer1[1], |
|||
out TValue output0, |
|||
out TValue output4, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer1[3], |
|||
buffer1[2], |
|||
out TValue output2, |
|||
out TValue output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[4], buffer1[5], out buffer0[4], out buffer0[5]); |
|||
Av1ForwardTransformArithmetic<TValue>.AddSubtract(buffer1[7], buffer1[6], out buffer0[7], out buffer0[6]); |
|||
|
|||
// Highway fuses the final two stages because no intermediate value is reused after either rotation.
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer0[7], |
|||
buffer0[4], |
|||
out TValue output1, |
|||
out TValue output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<TValue>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer0[6], |
|||
buffer0[5], |
|||
out TValue output5, |
|||
out TValue output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
} |
|||
} |
|||
@ -1,135 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
/// <content>
|
|||
/// Implements the length-specific forward identity transform scaling.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformOperations |
|||
{ |
|||
/// <summary>
|
|||
/// Applies the four-point forward identity transform to every independent 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 void Identity4<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Identity(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit, 4, 1, 0); |
|||
|
|||
/// <summary>
|
|||
/// Applies the eight-point forward identity transform to every independent 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 void Identity8<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Identity(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit, 8, 0, 1); |
|||
|
|||
/// <summary>
|
|||
/// Applies the sixteen-point forward identity transform to every independent 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 void Identity16<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Identity(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit, 16, 2, 0); |
|||
|
|||
/// <summary>
|
|||
/// Applies the thirty-two-point forward identity transform to every independent 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 void Identity32<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit) |
|||
where TValue : struct |
|||
=> Identity(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit, 32, 0, 2); |
|||
|
|||
/// <summary>
|
|||
/// Applies the length-specific AV1 identity scaling directly to the strided transform block.
|
|||
/// </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>
|
|||
/// <param name="length">The number of transform positions.</param>
|
|||
/// <param name="sqrt2Scale">The square-root-of-two multiplier, or zero when power-of-two scaling applies.</param>
|
|||
/// <param name="leftShift">The power-of-two scaling shift.</param>
|
|||
private static void Identity<TValue>( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<TValue> buffer0, |
|||
ref Av1TransformVector<TValue> buffer1, |
|||
int cosBit, |
|||
int length, |
|||
int sqrt2Scale, |
|||
int leftShift) |
|||
where TValue : struct |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// AV1 defines identity normalization by transform length: 4 and 16 use sqrt(2) scaling, while 8 and 32
|
|||
// are exact powers of two. Applying it in place matches Highway's row-oriented identity kernels.
|
|||
for (int i = 0; i < length; i++) |
|||
{ |
|||
TValue input = Load<TValue>(ref values, inputStride, i); |
|||
TValue output = sqrt2Scale != 0 |
|||
? Av1ForwardTransformArithmetic<TValue>.MultiplyRound( |
|||
input, |
|||
sqrt2Scale * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits) |
|||
: Av1ForwardTransformArithmetic<TValue>.ShiftLeft(input, leftShift); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,411 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the four-point forward ADST operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the four-point forward asymmetric discrete sine transform.
|
|||
/// </summary>
|
|||
internal readonly struct Adst4Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<int> buffer0, |
|||
ref Av1TransformVector<int> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
|
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<int>.CreateRounding(cosBit); |
|||
int input0 = Load<int>(ref values, inputStride, 0); |
|||
int input1 = Load<int>(ref values, inputStride, 1); |
|||
int input2 = Load<int>(ref values, inputStride, 2); |
|||
int input3 = Load<int>(ref values, inputStride, 3); |
|||
int input01 = Av1ForwardTransformArithmetic<int>.Add(input0, input1); |
|||
|
|||
// Packed lanes form input0 + input1 before widening, matching Highway's observable saturating arithmetic.
|
|||
int output0 = Av1ForwardTransformArithmetic<int>.MultiplyAddRound( |
|||
sinpi[1], input0, sinpi[2], input1, sinpi[3], input2, sinpi[4], input3, cosBit, in rounding); |
|||
|
|||
int output1 = Av1ForwardTransformArithmetic<int>.MultiplyAddRound( |
|||
sinpi[3], input01, -sinpi[3], input3, 0, input0, 0, input0, cosBit, in rounding); |
|||
|
|||
int output2 = Av1ForwardTransformArithmetic<int>.MultiplyAddRound( |
|||
sinpi[4], input0, -sinpi[1], input1, -sinpi[3], input2, sinpi[2], input3, cosBit, in rounding); |
|||
|
|||
// This expression preserves Highway's widened w2 - w0 + 3 * v5 sequence with one rounding point.
|
|||
int output3 = Av1ForwardTransformArithmetic<int>.MultiplyAddRound( |
|||
sinpi[4] - sinpi[1], |
|||
input0, |
|||
-sinpi[1] - sinpi[2], |
|||
input1, |
|||
sinpi[3], |
|||
input2, |
|||
sinpi[2] - sinpi[4], |
|||
input3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<short> buffer0, |
|||
ref Av1TransformVector<short> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
|
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<short>.CreateRounding(cosBit); |
|||
short input0 = Load<short>(ref values, inputStride, 0); |
|||
short input1 = Load<short>(ref values, inputStride, 1); |
|||
short input2 = Load<short>(ref values, inputStride, 2); |
|||
short input3 = Load<short>(ref values, inputStride, 3); |
|||
short input01 = Av1ForwardTransformArithmetic<short>.Add(input0, input1); |
|||
|
|||
// Packed lanes form input0 + input1 before widening, matching Highway's observable saturating arithmetic.
|
|||
short output0 = Av1ForwardTransformArithmetic<short>.MultiplyAddRound( |
|||
sinpi[1], input0, sinpi[2], input1, sinpi[3], input2, sinpi[4], input3, cosBit, in rounding); |
|||
|
|||
short output1 = Av1ForwardTransformArithmetic<short>.MultiplyAddRound( |
|||
sinpi[3], input01, -sinpi[3], input3, 0, input0, 0, input0, cosBit, in rounding); |
|||
|
|||
short output2 = Av1ForwardTransformArithmetic<short>.MultiplyAddRound( |
|||
sinpi[4], input0, -sinpi[1], input1, -sinpi[3], input2, sinpi[2], input3, cosBit, in rounding); |
|||
|
|||
// This expression preserves Highway's widened w2 - w0 + 3 * v5 sequence with one rounding point.
|
|||
short output3 = Av1ForwardTransformArithmetic<short>.MultiplyAddRound( |
|||
sinpi[4] - sinpi[1], |
|||
input0, |
|||
-sinpi[1] - sinpi[2], |
|||
input1, |
|||
sinpi[3], |
|||
input2, |
|||
sinpi[2] - sinpi[4], |
|||
input3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<short>> buffer0, |
|||
ref Av1TransformVector<Vector128<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
|
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector128<short>>.CreateRounding(cosBit); |
|||
Vector128<short> input0 = Load<Vector128<short>>(ref values, inputStride, 0); |
|||
Vector128<short> input1 = Load<Vector128<short>>(ref values, inputStride, 1); |
|||
Vector128<short> input2 = Load<Vector128<short>>(ref values, inputStride, 2); |
|||
Vector128<short> input3 = Load<Vector128<short>>(ref values, inputStride, 3); |
|||
Vector128<short> input01 = Av1ForwardTransformArithmetic<Vector128<short>>.Add(input0, input1); |
|||
|
|||
// Packed lanes form input0 + input1 before widening, matching Highway's observable saturating arithmetic.
|
|||
Vector128<short> output0 = Av1ForwardTransformArithmetic<Vector128<short>>.MultiplyAddRound( |
|||
sinpi[1], input0, sinpi[2], input1, sinpi[3], input2, sinpi[4], input3, cosBit, in rounding); |
|||
|
|||
Vector128<short> output1 = Av1ForwardTransformArithmetic<Vector128<short>>.MultiplyAddRound( |
|||
sinpi[3], input01, -sinpi[3], input3, 0, input0, 0, input0, cosBit, in rounding); |
|||
|
|||
Vector128<short> output2 = Av1ForwardTransformArithmetic<Vector128<short>>.MultiplyAddRound( |
|||
sinpi[4], input0, -sinpi[1], input1, -sinpi[3], input2, sinpi[2], input3, cosBit, in rounding); |
|||
|
|||
// This expression preserves Highway's widened w2 - w0 + 3 * v5 sequence with one rounding point.
|
|||
Vector128<short> output3 = Av1ForwardTransformArithmetic<Vector128<short>>.MultiplyAddRound( |
|||
sinpi[4] - sinpi[1], |
|||
input0, |
|||
-sinpi[1] - sinpi[2], |
|||
input1, |
|||
sinpi[3], |
|||
input2, |
|||
sinpi[2] - sinpi[4], |
|||
input3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<short>> buffer0, |
|||
ref Av1TransformVector<Vector256<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
|
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector256<short>>.CreateRounding(cosBit); |
|||
Vector256<short> input0 = Load<Vector256<short>>(ref values, inputStride, 0); |
|||
Vector256<short> input1 = Load<Vector256<short>>(ref values, inputStride, 1); |
|||
Vector256<short> input2 = Load<Vector256<short>>(ref values, inputStride, 2); |
|||
Vector256<short> input3 = Load<Vector256<short>>(ref values, inputStride, 3); |
|||
Vector256<short> input01 = Av1ForwardTransformArithmetic<Vector256<short>>.Add(input0, input1); |
|||
|
|||
// Packed lanes form input0 + input1 before widening, matching Highway's observable saturating arithmetic.
|
|||
Vector256<short> output0 = Av1ForwardTransformArithmetic<Vector256<short>>.MultiplyAddRound( |
|||
sinpi[1], input0, sinpi[2], input1, sinpi[3], input2, sinpi[4], input3, cosBit, in rounding); |
|||
|
|||
Vector256<short> output1 = Av1ForwardTransformArithmetic<Vector256<short>>.MultiplyAddRound( |
|||
sinpi[3], input01, -sinpi[3], input3, 0, input0, 0, input0, cosBit, in rounding); |
|||
|
|||
Vector256<short> output2 = Av1ForwardTransformArithmetic<Vector256<short>>.MultiplyAddRound( |
|||
sinpi[4], input0, -sinpi[1], input1, -sinpi[3], input2, sinpi[2], input3, cosBit, in rounding); |
|||
|
|||
// This expression preserves Highway's widened w2 - w0 + 3 * v5 sequence with one rounding point.
|
|||
Vector256<short> output3 = Av1ForwardTransformArithmetic<Vector256<short>>.MultiplyAddRound( |
|||
sinpi[4] - sinpi[1], |
|||
input0, |
|||
-sinpi[1] - sinpi[2], |
|||
input1, |
|||
sinpi[3], |
|||
input2, |
|||
sinpi[2] - sinpi[4], |
|||
input3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<short>> buffer0, |
|||
ref Av1TransformVector<Vector512<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
|
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector512<short>>.CreateRounding(cosBit); |
|||
Vector512<short> input0 = Load<Vector512<short>>(ref values, inputStride, 0); |
|||
Vector512<short> input1 = Load<Vector512<short>>(ref values, inputStride, 1); |
|||
Vector512<short> input2 = Load<Vector512<short>>(ref values, inputStride, 2); |
|||
Vector512<short> input3 = Load<Vector512<short>>(ref values, inputStride, 3); |
|||
Vector512<short> input01 = Av1ForwardTransformArithmetic<Vector512<short>>.Add(input0, input1); |
|||
|
|||
// Packed lanes form input0 + input1 before widening, matching Highway's observable saturating arithmetic.
|
|||
Vector512<short> output0 = Av1ForwardTransformArithmetic<Vector512<short>>.MultiplyAddRound( |
|||
sinpi[1], input0, sinpi[2], input1, sinpi[3], input2, sinpi[4], input3, cosBit, in rounding); |
|||
|
|||
Vector512<short> output1 = Av1ForwardTransformArithmetic<Vector512<short>>.MultiplyAddRound( |
|||
sinpi[3], input01, -sinpi[3], input3, 0, input0, 0, input0, cosBit, in rounding); |
|||
|
|||
Vector512<short> output2 = Av1ForwardTransformArithmetic<Vector512<short>>.MultiplyAddRound( |
|||
sinpi[4], input0, -sinpi[1], input1, -sinpi[3], input2, sinpi[2], input3, cosBit, in rounding); |
|||
|
|||
// This expression preserves Highway's widened w2 - w0 + 3 * v5 sequence with one rounding point.
|
|||
Vector512<short> output3 = Av1ForwardTransformArithmetic<Vector512<short>>.MultiplyAddRound( |
|||
sinpi[4] - sinpi[1], |
|||
input0, |
|||
-sinpi[1] - sinpi[2], |
|||
input1, |
|||
sinpi[3], |
|||
input2, |
|||
sinpi[2] - sinpi[4], |
|||
input3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<int>> buffer0, |
|||
ref Av1TransformVector<Vector128<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
|
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector128<int>>.CreateRounding(cosBit); |
|||
Vector128<int> input0 = Load<Vector128<int>>(ref values, inputStride, 0); |
|||
Vector128<int> input1 = Load<Vector128<int>>(ref values, inputStride, 1); |
|||
Vector128<int> input2 = Load<Vector128<int>>(ref values, inputStride, 2); |
|||
Vector128<int> input3 = Load<Vector128<int>>(ref values, inputStride, 3); |
|||
Vector128<int> input01 = Av1ForwardTransformArithmetic<Vector128<int>>.Add(input0, input1); |
|||
|
|||
// Packed lanes form input0 + input1 before widening, matching Highway's observable saturating arithmetic.
|
|||
Vector128<int> output0 = Av1ForwardTransformArithmetic<Vector128<int>>.MultiplyAddRound( |
|||
sinpi[1], input0, sinpi[2], input1, sinpi[3], input2, sinpi[4], input3, cosBit, in rounding); |
|||
|
|||
Vector128<int> output1 = Av1ForwardTransformArithmetic<Vector128<int>>.MultiplyAddRound( |
|||
sinpi[3], input01, -sinpi[3], input3, 0, input0, 0, input0, cosBit, in rounding); |
|||
|
|||
Vector128<int> output2 = Av1ForwardTransformArithmetic<Vector128<int>>.MultiplyAddRound( |
|||
sinpi[4], input0, -sinpi[1], input1, -sinpi[3], input2, sinpi[2], input3, cosBit, in rounding); |
|||
|
|||
// This expression preserves Highway's widened w2 - w0 + 3 * v5 sequence with one rounding point.
|
|||
Vector128<int> output3 = Av1ForwardTransformArithmetic<Vector128<int>>.MultiplyAddRound( |
|||
sinpi[4] - sinpi[1], |
|||
input0, |
|||
-sinpi[1] - sinpi[2], |
|||
input1, |
|||
sinpi[3], |
|||
input2, |
|||
sinpi[2] - sinpi[4], |
|||
input3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<int>> buffer0, |
|||
ref Av1TransformVector<Vector256<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
|
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector256<int>>.CreateRounding(cosBit); |
|||
Vector256<int> input0 = Load<Vector256<int>>(ref values, inputStride, 0); |
|||
Vector256<int> input1 = Load<Vector256<int>>(ref values, inputStride, 1); |
|||
Vector256<int> input2 = Load<Vector256<int>>(ref values, inputStride, 2); |
|||
Vector256<int> input3 = Load<Vector256<int>>(ref values, inputStride, 3); |
|||
Vector256<int> input01 = Av1ForwardTransformArithmetic<Vector256<int>>.Add(input0, input1); |
|||
|
|||
// Packed lanes form input0 + input1 before widening, matching Highway's observable saturating arithmetic.
|
|||
Vector256<int> output0 = Av1ForwardTransformArithmetic<Vector256<int>>.MultiplyAddRound( |
|||
sinpi[1], input0, sinpi[2], input1, sinpi[3], input2, sinpi[4], input3, cosBit, in rounding); |
|||
|
|||
Vector256<int> output1 = Av1ForwardTransformArithmetic<Vector256<int>>.MultiplyAddRound( |
|||
sinpi[3], input01, -sinpi[3], input3, 0, input0, 0, input0, cosBit, in rounding); |
|||
|
|||
Vector256<int> output2 = Av1ForwardTransformArithmetic<Vector256<int>>.MultiplyAddRound( |
|||
sinpi[4], input0, -sinpi[1], input1, -sinpi[3], input2, sinpi[2], input3, cosBit, in rounding); |
|||
|
|||
// This expression preserves Highway's widened w2 - w0 + 3 * v5 sequence with one rounding point.
|
|||
Vector256<int> output3 = Av1ForwardTransformArithmetic<Vector256<int>>.MultiplyAddRound( |
|||
sinpi[4] - sinpi[1], |
|||
input0, |
|||
-sinpi[1] - sinpi[2], |
|||
input1, |
|||
sinpi[3], |
|||
input2, |
|||
sinpi[2] - sinpi[4], |
|||
input3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<int>> buffer0, |
|||
ref Av1TransformVector<Vector512<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
|
|||
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector512<int>>.CreateRounding(cosBit); |
|||
Vector512<int> input0 = Load<Vector512<int>>(ref values, inputStride, 0); |
|||
Vector512<int> input1 = Load<Vector512<int>>(ref values, inputStride, 1); |
|||
Vector512<int> input2 = Load<Vector512<int>>(ref values, inputStride, 2); |
|||
Vector512<int> input3 = Load<Vector512<int>>(ref values, inputStride, 3); |
|||
Vector512<int> input01 = Av1ForwardTransformArithmetic<Vector512<int>>.Add(input0, input1); |
|||
|
|||
// Packed lanes form input0 + input1 before widening, matching Highway's observable saturating arithmetic.
|
|||
Vector512<int> output0 = Av1ForwardTransformArithmetic<Vector512<int>>.MultiplyAddRound( |
|||
sinpi[1], input0, sinpi[2], input1, sinpi[3], input2, sinpi[4], input3, cosBit, in rounding); |
|||
|
|||
Vector512<int> output1 = Av1ForwardTransformArithmetic<Vector512<int>>.MultiplyAddRound( |
|||
sinpi[3], input01, -sinpi[3], input3, 0, input0, 0, input0, cosBit, in rounding); |
|||
|
|||
Vector512<int> output2 = Av1ForwardTransformArithmetic<Vector512<int>>.MultiplyAddRound( |
|||
sinpi[4], input0, -sinpi[1], input1, -sinpi[3], input2, sinpi[2], input3, cosBit, in rounding); |
|||
|
|||
// This expression preserves Highway's widened w2 - w0 + 3 * v5 sequence with one rounding point.
|
|||
Vector512<int> output3 = Av1ForwardTransformArithmetic<Vector512<int>>.MultiplyAddRound( |
|||
sinpi[4] - sinpi[1], |
|||
input0, |
|||
-sinpi[1] - sinpi[2], |
|||
input1, |
|||
sinpi[3], |
|||
input2, |
|||
sinpi[2] - sinpi[4], |
|||
input3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,656 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the eight-point forward ADST operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the eight-point forward asymmetric discrete sine transform.
|
|||
/// </summary>
|
|||
internal readonly struct Adst8Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the fixed coefficient permutation.
|
|||
/// </summary>
|
|||
private static ReadOnlySpan<byte> OutputOrder => [1, 6, 3, 4, 5, 2, 7, 0]; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<int> buffer0, |
|||
ref Av1TransformVector<int> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<int>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 applies the ADST permutation and signs while the source block is still read-only.
|
|||
buffer0[0] = Load<int>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<int>.Negate(Load<int>(ref values, inputStride, 7)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<int>.Negate(Load<int>(ref values, inputStride, 3)); |
|||
buffer0[3] = Load<int>(ref values, inputStride, 4); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<int>.Negate(Load<int>(ref values, inputStride, 1)); |
|||
buffer0[5] = Load<int>(ref values, inputStride, 6); |
|||
buffer0[6] = Load<int>(ref values, inputStride, 2); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<int>.Negate(Load<int>(ref values, inputStride, 5)); |
|||
|
|||
// Stage 2 rotates the second pair in each four-value group while copying the already aligned pairs.
|
|||
buffer1[0] = buffer0[0]; |
|||
buffer1[1] = buffer0[1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[2], buffer0[3], ref buffer1, 2, 3, cosBit, in rounding); |
|||
buffer1[4] = buffer0[4]; |
|||
buffer1[5] = buffer0[5]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[6], buffer0[7], ref buffer1, 6, 7, cosBit, in rounding); |
|||
|
|||
// Stage 3 combines the rotated and copied pairs into two independent four-value groups.
|
|||
for (int group = 0; group < 8; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<int>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper group by pi/8 while the completed lower group passes through unchanged.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[4] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[16], buffer0[4], cospi[48], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[48], buffer0[4], -cospi[16], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<int>.HalfButterfly(-cospi[48], buffer0[6], cospi[16], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[16], buffer0[6], cospi[48], buffer0[7], cosBit, in rounding); |
|||
|
|||
// Stage 5 creates the four final butterfly pairs spanning the two groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<int>.AddSubtract(buffer1[i], buffer1[i + 4], out buffer0[i], out buffer0[i + 4]); |
|||
} |
|||
|
|||
// Stage 6 applies the remaining odd-angle rotations.
|
|||
buffer1[0] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[4], buffer0[0], cospi[60], buffer0[1], cosBit, in rounding); |
|||
buffer1[1] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[60], buffer0[0], -cospi[4], buffer0[1], cosBit, in rounding); |
|||
buffer1[2] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[20], buffer0[2], cospi[44], buffer0[3], cosBit, in rounding); |
|||
buffer1[3] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[44], buffer0[2], -cospi[20], buffer0[3], cosBit, in rounding); |
|||
buffer1[4] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[36], buffer0[4], cospi[28], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[28], buffer0[4], -cospi[36], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[52], buffer0[6], cospi[12], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<int>.HalfButterfly(cospi[12], buffer0[6], -cospi[52], buffer0[7], cosBit, in rounding); |
|||
|
|||
ReadOnlySpan<byte> outputOrder = OutputOrder; |
|||
|
|||
// Stage 7 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<short> buffer0, |
|||
ref Av1TransformVector<short> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<short>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 applies the ADST permutation and signs while the source block is still read-only.
|
|||
buffer0[0] = Load<short>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<short>.Negate(Load<short>(ref values, inputStride, 7)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<short>.Negate(Load<short>(ref values, inputStride, 3)); |
|||
buffer0[3] = Load<short>(ref values, inputStride, 4); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<short>.Negate(Load<short>(ref values, inputStride, 1)); |
|||
buffer0[5] = Load<short>(ref values, inputStride, 6); |
|||
buffer0[6] = Load<short>(ref values, inputStride, 2); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<short>.Negate(Load<short>(ref values, inputStride, 5)); |
|||
|
|||
// Stage 2 rotates the second pair in each four-value group while copying the already aligned pairs.
|
|||
buffer1[0] = buffer0[0]; |
|||
buffer1[1] = buffer0[1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[2], buffer0[3], ref buffer1, 2, 3, cosBit, in rounding); |
|||
buffer1[4] = buffer0[4]; |
|||
buffer1[5] = buffer0[5]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[6], buffer0[7], ref buffer1, 6, 7, cosBit, in rounding); |
|||
|
|||
// Stage 3 combines the rotated and copied pairs into two independent four-value groups.
|
|||
for (int group = 0; group < 8; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<short>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper group by pi/8 while the completed lower group passes through unchanged.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[4] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[16], buffer0[4], cospi[48], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[48], buffer0[4], -cospi[16], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<short>.HalfButterfly(-cospi[48], buffer0[6], cospi[16], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[16], buffer0[6], cospi[48], buffer0[7], cosBit, in rounding); |
|||
|
|||
// Stage 5 creates the four final butterfly pairs spanning the two groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<short>.AddSubtract(buffer1[i], buffer1[i + 4], out buffer0[i], out buffer0[i + 4]); |
|||
} |
|||
|
|||
// Stage 6 applies the remaining odd-angle rotations.
|
|||
buffer1[0] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[4], buffer0[0], cospi[60], buffer0[1], cosBit, in rounding); |
|||
buffer1[1] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[60], buffer0[0], -cospi[4], buffer0[1], cosBit, in rounding); |
|||
buffer1[2] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[20], buffer0[2], cospi[44], buffer0[3], cosBit, in rounding); |
|||
buffer1[3] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[44], buffer0[2], -cospi[20], buffer0[3], cosBit, in rounding); |
|||
buffer1[4] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[36], buffer0[4], cospi[28], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[28], buffer0[4], -cospi[36], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[52], buffer0[6], cospi[12], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<short>.HalfButterfly(cospi[12], buffer0[6], -cospi[52], buffer0[7], cosBit, in rounding); |
|||
|
|||
ReadOnlySpan<byte> outputOrder = OutputOrder; |
|||
|
|||
// Stage 7 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<short>> buffer0, |
|||
ref Av1TransformVector<Vector128<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector128<short>>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 applies the ADST permutation and signs while the source block is still read-only.
|
|||
buffer0[0] = Load<Vector128<short>>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<Vector128<short>>.Negate(Load<Vector128<short>>(ref values, inputStride, 7)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<Vector128<short>>.Negate(Load<Vector128<short>>(ref values, inputStride, 3)); |
|||
buffer0[3] = Load<Vector128<short>>(ref values, inputStride, 4); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<Vector128<short>>.Negate(Load<Vector128<short>>(ref values, inputStride, 1)); |
|||
buffer0[5] = Load<Vector128<short>>(ref values, inputStride, 6); |
|||
buffer0[6] = Load<Vector128<short>>(ref values, inputStride, 2); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<Vector128<short>>.Negate(Load<Vector128<short>>(ref values, inputStride, 5)); |
|||
|
|||
// Stage 2 rotates the second pair in each four-value group while copying the already aligned pairs.
|
|||
buffer1[0] = buffer0[0]; |
|||
buffer1[1] = buffer0[1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[2], buffer0[3], ref buffer1, 2, 3, cosBit, in rounding); |
|||
buffer1[4] = buffer0[4]; |
|||
buffer1[5] = buffer0[5]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[6], buffer0[7], ref buffer1, 6, 7, cosBit, in rounding); |
|||
|
|||
// Stage 3 combines the rotated and copied pairs into two independent four-value groups.
|
|||
for (int group = 0; group < 8; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper group by pi/8 while the completed lower group passes through unchanged.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[16], buffer0[4], cospi[48], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[48], buffer0[4], -cospi[16], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(-cospi[48], buffer0[6], cospi[16], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[16], buffer0[6], cospi[48], buffer0[7], cosBit, in rounding); |
|||
|
|||
// Stage 5 creates the four final butterfly pairs spanning the two groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract(buffer1[i], buffer1[i + 4], out buffer0[i], out buffer0[i + 4]); |
|||
} |
|||
|
|||
// Stage 6 applies the remaining odd-angle rotations.
|
|||
buffer1[0] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[4], buffer0[0], cospi[60], buffer0[1], cosBit, in rounding); |
|||
buffer1[1] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[60], buffer0[0], -cospi[4], buffer0[1], cosBit, in rounding); |
|||
buffer1[2] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[20], buffer0[2], cospi[44], buffer0[3], cosBit, in rounding); |
|||
buffer1[3] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[44], buffer0[2], -cospi[20], buffer0[3], cosBit, in rounding); |
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[36], buffer0[4], cospi[28], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[28], buffer0[4], -cospi[36], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[52], buffer0[6], cospi[12], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector128<short>>.HalfButterfly(cospi[12], buffer0[6], -cospi[52], buffer0[7], cosBit, in rounding); |
|||
|
|||
ReadOnlySpan<byte> outputOrder = OutputOrder; |
|||
|
|||
// Stage 7 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<short>> buffer0, |
|||
ref Av1TransformVector<Vector256<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector256<short>>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 applies the ADST permutation and signs while the source block is still read-only.
|
|||
buffer0[0] = Load<Vector256<short>>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<Vector256<short>>.Negate(Load<Vector256<short>>(ref values, inputStride, 7)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<Vector256<short>>.Negate(Load<Vector256<short>>(ref values, inputStride, 3)); |
|||
buffer0[3] = Load<Vector256<short>>(ref values, inputStride, 4); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<Vector256<short>>.Negate(Load<Vector256<short>>(ref values, inputStride, 1)); |
|||
buffer0[5] = Load<Vector256<short>>(ref values, inputStride, 6); |
|||
buffer0[6] = Load<Vector256<short>>(ref values, inputStride, 2); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<Vector256<short>>.Negate(Load<Vector256<short>>(ref values, inputStride, 5)); |
|||
|
|||
// Stage 2 rotates the second pair in each four-value group while copying the already aligned pairs.
|
|||
buffer1[0] = buffer0[0]; |
|||
buffer1[1] = buffer0[1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[2], buffer0[3], ref buffer1, 2, 3, cosBit, in rounding); |
|||
buffer1[4] = buffer0[4]; |
|||
buffer1[5] = buffer0[5]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[6], buffer0[7], ref buffer1, 6, 7, cosBit, in rounding); |
|||
|
|||
// Stage 3 combines the rotated and copied pairs into two independent four-value groups.
|
|||
for (int group = 0; group < 8; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper group by pi/8 while the completed lower group passes through unchanged.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[16], buffer0[4], cospi[48], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[48], buffer0[4], -cospi[16], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(-cospi[48], buffer0[6], cospi[16], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[16], buffer0[6], cospi[48], buffer0[7], cosBit, in rounding); |
|||
|
|||
// Stage 5 creates the four final butterfly pairs spanning the two groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract(buffer1[i], buffer1[i + 4], out buffer0[i], out buffer0[i + 4]); |
|||
} |
|||
|
|||
// Stage 6 applies the remaining odd-angle rotations.
|
|||
buffer1[0] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[4], buffer0[0], cospi[60], buffer0[1], cosBit, in rounding); |
|||
buffer1[1] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[60], buffer0[0], -cospi[4], buffer0[1], cosBit, in rounding); |
|||
buffer1[2] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[20], buffer0[2], cospi[44], buffer0[3], cosBit, in rounding); |
|||
buffer1[3] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[44], buffer0[2], -cospi[20], buffer0[3], cosBit, in rounding); |
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[36], buffer0[4], cospi[28], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[28], buffer0[4], -cospi[36], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[52], buffer0[6], cospi[12], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector256<short>>.HalfButterfly(cospi[12], buffer0[6], -cospi[52], buffer0[7], cosBit, in rounding); |
|||
|
|||
ReadOnlySpan<byte> outputOrder = OutputOrder; |
|||
|
|||
// Stage 7 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<short>> buffer0, |
|||
ref Av1TransformVector<Vector512<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector512<short>>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 applies the ADST permutation and signs while the source block is still read-only.
|
|||
buffer0[0] = Load<Vector512<short>>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<Vector512<short>>.Negate(Load<Vector512<short>>(ref values, inputStride, 7)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<Vector512<short>>.Negate(Load<Vector512<short>>(ref values, inputStride, 3)); |
|||
buffer0[3] = Load<Vector512<short>>(ref values, inputStride, 4); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<Vector512<short>>.Negate(Load<Vector512<short>>(ref values, inputStride, 1)); |
|||
buffer0[5] = Load<Vector512<short>>(ref values, inputStride, 6); |
|||
buffer0[6] = Load<Vector512<short>>(ref values, inputStride, 2); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<Vector512<short>>.Negate(Load<Vector512<short>>(ref values, inputStride, 5)); |
|||
|
|||
// Stage 2 rotates the second pair in each four-value group while copying the already aligned pairs.
|
|||
buffer1[0] = buffer0[0]; |
|||
buffer1[1] = buffer0[1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[2], buffer0[3], ref buffer1, 2, 3, cosBit, in rounding); |
|||
buffer1[4] = buffer0[4]; |
|||
buffer1[5] = buffer0[5]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[6], buffer0[7], ref buffer1, 6, 7, cosBit, in rounding); |
|||
|
|||
// Stage 3 combines the rotated and copied pairs into two independent four-value groups.
|
|||
for (int group = 0; group < 8; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper group by pi/8 while the completed lower group passes through unchanged.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[16], buffer0[4], cospi[48], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[48], buffer0[4], -cospi[16], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(-cospi[48], buffer0[6], cospi[16], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[16], buffer0[6], cospi[48], buffer0[7], cosBit, in rounding); |
|||
|
|||
// Stage 5 creates the four final butterfly pairs spanning the two groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract(buffer1[i], buffer1[i + 4], out buffer0[i], out buffer0[i + 4]); |
|||
} |
|||
|
|||
// Stage 6 applies the remaining odd-angle rotations.
|
|||
buffer1[0] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[4], buffer0[0], cospi[60], buffer0[1], cosBit, in rounding); |
|||
buffer1[1] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[60], buffer0[0], -cospi[4], buffer0[1], cosBit, in rounding); |
|||
buffer1[2] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[20], buffer0[2], cospi[44], buffer0[3], cosBit, in rounding); |
|||
buffer1[3] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[44], buffer0[2], -cospi[20], buffer0[3], cosBit, in rounding); |
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[36], buffer0[4], cospi[28], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[28], buffer0[4], -cospi[36], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[52], buffer0[6], cospi[12], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector512<short>>.HalfButterfly(cospi[12], buffer0[6], -cospi[52], buffer0[7], cosBit, in rounding); |
|||
|
|||
ReadOnlySpan<byte> outputOrder = OutputOrder; |
|||
|
|||
// Stage 7 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<int>> buffer0, |
|||
ref Av1TransformVector<Vector128<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector128<int>>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 applies the ADST permutation and signs while the source block is still read-only.
|
|||
buffer0[0] = Load<Vector128<int>>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<Vector128<int>>.Negate(Load<Vector128<int>>(ref values, inputStride, 7)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<Vector128<int>>.Negate(Load<Vector128<int>>(ref values, inputStride, 3)); |
|||
buffer0[3] = Load<Vector128<int>>(ref values, inputStride, 4); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<Vector128<int>>.Negate(Load<Vector128<int>>(ref values, inputStride, 1)); |
|||
buffer0[5] = Load<Vector128<int>>(ref values, inputStride, 6); |
|||
buffer0[6] = Load<Vector128<int>>(ref values, inputStride, 2); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<Vector128<int>>.Negate(Load<Vector128<int>>(ref values, inputStride, 5)); |
|||
|
|||
// Stage 2 rotates the second pair in each four-value group while copying the already aligned pairs.
|
|||
buffer1[0] = buffer0[0]; |
|||
buffer1[1] = buffer0[1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[2], buffer0[3], ref buffer1, 2, 3, cosBit, in rounding); |
|||
buffer1[4] = buffer0[4]; |
|||
buffer1[5] = buffer0[5]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[6], buffer0[7], ref buffer1, 6, 7, cosBit, in rounding); |
|||
|
|||
// Stage 3 combines the rotated and copied pairs into two independent four-value groups.
|
|||
for (int group = 0; group < 8; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper group by pi/8 while the completed lower group passes through unchanged.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[16], buffer0[4], cospi[48], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[48], buffer0[4], -cospi[16], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(-cospi[48], buffer0[6], cospi[16], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[16], buffer0[6], cospi[48], buffer0[7], cosBit, in rounding); |
|||
|
|||
// Stage 5 creates the four final butterfly pairs spanning the two groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract(buffer1[i], buffer1[i + 4], out buffer0[i], out buffer0[i + 4]); |
|||
} |
|||
|
|||
// Stage 6 applies the remaining odd-angle rotations.
|
|||
buffer1[0] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[4], buffer0[0], cospi[60], buffer0[1], cosBit, in rounding); |
|||
buffer1[1] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[60], buffer0[0], -cospi[4], buffer0[1], cosBit, in rounding); |
|||
buffer1[2] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[20], buffer0[2], cospi[44], buffer0[3], cosBit, in rounding); |
|||
buffer1[3] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[44], buffer0[2], -cospi[20], buffer0[3], cosBit, in rounding); |
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[36], buffer0[4], cospi[28], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[28], buffer0[4], -cospi[36], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[52], buffer0[6], cospi[12], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector128<int>>.HalfButterfly(cospi[12], buffer0[6], -cospi[52], buffer0[7], cosBit, in rounding); |
|||
|
|||
ReadOnlySpan<byte> outputOrder = OutputOrder; |
|||
|
|||
// Stage 7 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<int>> buffer0, |
|||
ref Av1TransformVector<Vector256<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector256<int>>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 applies the ADST permutation and signs while the source block is still read-only.
|
|||
buffer0[0] = Load<Vector256<int>>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<Vector256<int>>.Negate(Load<Vector256<int>>(ref values, inputStride, 7)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<Vector256<int>>.Negate(Load<Vector256<int>>(ref values, inputStride, 3)); |
|||
buffer0[3] = Load<Vector256<int>>(ref values, inputStride, 4); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<Vector256<int>>.Negate(Load<Vector256<int>>(ref values, inputStride, 1)); |
|||
buffer0[5] = Load<Vector256<int>>(ref values, inputStride, 6); |
|||
buffer0[6] = Load<Vector256<int>>(ref values, inputStride, 2); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<Vector256<int>>.Negate(Load<Vector256<int>>(ref values, inputStride, 5)); |
|||
|
|||
// Stage 2 rotates the second pair in each four-value group while copying the already aligned pairs.
|
|||
buffer1[0] = buffer0[0]; |
|||
buffer1[1] = buffer0[1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[2], buffer0[3], ref buffer1, 2, 3, cosBit, in rounding); |
|||
buffer1[4] = buffer0[4]; |
|||
buffer1[5] = buffer0[5]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[6], buffer0[7], ref buffer1, 6, 7, cosBit, in rounding); |
|||
|
|||
// Stage 3 combines the rotated and copied pairs into two independent four-value groups.
|
|||
for (int group = 0; group < 8; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper group by pi/8 while the completed lower group passes through unchanged.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[16], buffer0[4], cospi[48], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[48], buffer0[4], -cospi[16], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(-cospi[48], buffer0[6], cospi[16], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[16], buffer0[6], cospi[48], buffer0[7], cosBit, in rounding); |
|||
|
|||
// Stage 5 creates the four final butterfly pairs spanning the two groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract(buffer1[i], buffer1[i + 4], out buffer0[i], out buffer0[i + 4]); |
|||
} |
|||
|
|||
// Stage 6 applies the remaining odd-angle rotations.
|
|||
buffer1[0] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[4], buffer0[0], cospi[60], buffer0[1], cosBit, in rounding); |
|||
buffer1[1] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[60], buffer0[0], -cospi[4], buffer0[1], cosBit, in rounding); |
|||
buffer1[2] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[20], buffer0[2], cospi[44], buffer0[3], cosBit, in rounding); |
|||
buffer1[3] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[44], buffer0[2], -cospi[20], buffer0[3], cosBit, in rounding); |
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[36], buffer0[4], cospi[28], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[28], buffer0[4], -cospi[36], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[52], buffer0[6], cospi[12], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector256<int>>.HalfButterfly(cospi[12], buffer0[6], -cospi[52], buffer0[7], cosBit, in rounding); |
|||
|
|||
ReadOnlySpan<byte> outputOrder = OutputOrder; |
|||
|
|||
// Stage 7 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<int>> buffer0, |
|||
ref Av1TransformVector<Vector512<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector512<int>>.CreateRounding(cosBit); |
|||
|
|||
// Stage 1 applies the ADST permutation and signs while the source block is still read-only.
|
|||
buffer0[0] = Load<Vector512<int>>(ref values, inputStride, 0); |
|||
buffer0[1] = Av1ForwardTransformArithmetic<Vector512<int>>.Negate(Load<Vector512<int>>(ref values, inputStride, 7)); |
|||
buffer0[2] = Av1ForwardTransformArithmetic<Vector512<int>>.Negate(Load<Vector512<int>>(ref values, inputStride, 3)); |
|||
buffer0[3] = Load<Vector512<int>>(ref values, inputStride, 4); |
|||
buffer0[4] = Av1ForwardTransformArithmetic<Vector512<int>>.Negate(Load<Vector512<int>>(ref values, inputStride, 1)); |
|||
buffer0[5] = Load<Vector512<int>>(ref values, inputStride, 6); |
|||
buffer0[6] = Load<Vector512<int>>(ref values, inputStride, 2); |
|||
buffer0[7] = Av1ForwardTransformArithmetic<Vector512<int>>.Negate(Load<Vector512<int>>(ref values, inputStride, 5)); |
|||
|
|||
// Stage 2 rotates the second pair in each four-value group while copying the already aligned pairs.
|
|||
buffer1[0] = buffer0[0]; |
|||
buffer1[1] = buffer0[1]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[2], buffer0[3], ref buffer1, 2, 3, cosBit, in rounding); |
|||
buffer1[4] = buffer0[4]; |
|||
buffer1[5] = buffer0[5]; |
|||
Butterfly(cospi[32], cospi[32], buffer0[6], buffer0[7], ref buffer1, 6, 7, cosBit, in rounding); |
|||
|
|||
// Stage 3 combines the rotated and copied pairs into two independent four-value groups.
|
|||
for (int group = 0; group < 8; group += 4) |
|||
{ |
|||
for (int i = 0; i < 2; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract( |
|||
buffer1[group + i], |
|||
buffer1[group + i + 2], |
|||
out buffer0[group + i], |
|||
out buffer0[group + i + 2]); |
|||
} |
|||
} |
|||
|
|||
// Stage 4 rotates the upper group by pi/8 while the completed lower group passes through unchanged.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
buffer1[i] = buffer0[i]; |
|||
} |
|||
|
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[16], buffer0[4], cospi[48], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[48], buffer0[4], -cospi[16], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(-cospi[48], buffer0[6], cospi[16], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[16], buffer0[6], cospi[48], buffer0[7], cosBit, in rounding); |
|||
|
|||
// Stage 5 creates the four final butterfly pairs spanning the two groups.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract(buffer1[i], buffer1[i + 4], out buffer0[i], out buffer0[i + 4]); |
|||
} |
|||
|
|||
// Stage 6 applies the remaining odd-angle rotations.
|
|||
buffer1[0] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[4], buffer0[0], cospi[60], buffer0[1], cosBit, in rounding); |
|||
buffer1[1] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[60], buffer0[0], -cospi[4], buffer0[1], cosBit, in rounding); |
|||
buffer1[2] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[20], buffer0[2], cospi[44], buffer0[3], cosBit, in rounding); |
|||
buffer1[3] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[44], buffer0[2], -cospi[20], buffer0[3], cosBit, in rounding); |
|||
buffer1[4] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[36], buffer0[4], cospi[28], buffer0[5], cosBit, in rounding); |
|||
buffer1[5] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[28], buffer0[4], -cospi[36], buffer0[5], cosBit, in rounding); |
|||
buffer1[6] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[52], buffer0[6], cospi[12], buffer0[7], cosBit, in rounding); |
|||
buffer1[7] = Av1ForwardTransformArithmetic<Vector512<int>>.HalfButterfly(cospi[12], buffer0[6], -cospi[52], buffer0[7], cosBit, in rounding); |
|||
|
|||
ReadOnlySpan<byte> outputOrder = OutputOrder; |
|||
|
|||
// Stage 7 maps the rotated values to ascending AV1 ADST coefficient order.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Store(ref values, outputStride, i, buffer1[outputOrder[i]]); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,395 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the four-point forward DCT operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the four-point forward discrete cosine transform.
|
|||
/// </summary>
|
|||
internal readonly struct Dct4Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<int> buffer0, |
|||
ref Av1TransformVector<int> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<int>.CreateRounding(cosBit); |
|||
|
|||
int input0 = Load<int>(ref values, inputStride, 0); |
|||
int input1 = Load<int>(ref values, inputStride, 1); |
|||
int input2 = Load<int>(ref values, inputStride, 2); |
|||
int input3 = Load<int>(ref values, inputStride, 3); |
|||
|
|||
// The paired stage keeps the axes in their native lane representation. Packed short lanes therefore retain
|
|||
// Highway's saturating add/subtract behavior before the widening butterfly multiplication.
|
|||
Av1ForwardTransformArithmetic<int>.AddSubtract(input0, input3, out buffer0[0], out buffer0[3]); |
|||
Av1ForwardTransformArithmetic<int>.AddSubtract(input1, input2, out buffer0[1], out buffer0[2]); |
|||
Av1ForwardTransformArithmetic<int>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer0[0], |
|||
buffer0[1], |
|||
out int output0, |
|||
out int output2, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<int>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer0[3], |
|||
buffer0[2], |
|||
out int output1, |
|||
out int output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<short> buffer0, |
|||
ref Av1TransformVector<short> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<short>.CreateRounding(cosBit); |
|||
|
|||
short input0 = Load<short>(ref values, inputStride, 0); |
|||
short input1 = Load<short>(ref values, inputStride, 1); |
|||
short input2 = Load<short>(ref values, inputStride, 2); |
|||
short input3 = Load<short>(ref values, inputStride, 3); |
|||
|
|||
// The paired stage keeps the axes in their native lane representation. Packed short lanes therefore retain
|
|||
// Highway's saturating add/subtract behavior before the widening butterfly multiplication.
|
|||
Av1ForwardTransformArithmetic<short>.AddSubtract(input0, input3, out buffer0[0], out buffer0[3]); |
|||
Av1ForwardTransformArithmetic<short>.AddSubtract(input1, input2, out buffer0[1], out buffer0[2]); |
|||
Av1ForwardTransformArithmetic<short>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer0[0], |
|||
buffer0[1], |
|||
out short output0, |
|||
out short output2, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<short>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer0[3], |
|||
buffer0[2], |
|||
out short output1, |
|||
out short output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<short>> buffer0, |
|||
ref Av1TransformVector<Vector128<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector128<short>>.CreateRounding(cosBit); |
|||
|
|||
Vector128<short> input0 = Load<Vector128<short>>(ref values, inputStride, 0); |
|||
Vector128<short> input1 = Load<Vector128<short>>(ref values, inputStride, 1); |
|||
Vector128<short> input2 = Load<Vector128<short>>(ref values, inputStride, 2); |
|||
Vector128<short> input3 = Load<Vector128<short>>(ref values, inputStride, 3); |
|||
|
|||
// The paired stage keeps the axes in their native lane representation. Packed short lanes therefore retain
|
|||
// Highway's saturating add/subtract behavior before the widening butterfly multiplication.
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract(input0, input3, out buffer0[0], out buffer0[3]); |
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract(input1, input2, out buffer0[1], out buffer0[2]); |
|||
Av1ForwardTransformArithmetic<Vector128<short>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer0[0], |
|||
buffer0[1], |
|||
out Vector128<short> output0, |
|||
out Vector128<short> output2, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer0[3], |
|||
buffer0[2], |
|||
out Vector128<short> output1, |
|||
out Vector128<short> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<short>> buffer0, |
|||
ref Av1TransformVector<Vector256<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector256<short>>.CreateRounding(cosBit); |
|||
|
|||
Vector256<short> input0 = Load<Vector256<short>>(ref values, inputStride, 0); |
|||
Vector256<short> input1 = Load<Vector256<short>>(ref values, inputStride, 1); |
|||
Vector256<short> input2 = Load<Vector256<short>>(ref values, inputStride, 2); |
|||
Vector256<short> input3 = Load<Vector256<short>>(ref values, inputStride, 3); |
|||
|
|||
// The paired stage keeps the axes in their native lane representation. Packed short lanes therefore retain
|
|||
// Highway's saturating add/subtract behavior before the widening butterfly multiplication.
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract(input0, input3, out buffer0[0], out buffer0[3]); |
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract(input1, input2, out buffer0[1], out buffer0[2]); |
|||
Av1ForwardTransformArithmetic<Vector256<short>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer0[0], |
|||
buffer0[1], |
|||
out Vector256<short> output0, |
|||
out Vector256<short> output2, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer0[3], |
|||
buffer0[2], |
|||
out Vector256<short> output1, |
|||
out Vector256<short> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<short>> buffer0, |
|||
ref Av1TransformVector<Vector512<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector512<short>>.CreateRounding(cosBit); |
|||
|
|||
Vector512<short> input0 = Load<Vector512<short>>(ref values, inputStride, 0); |
|||
Vector512<short> input1 = Load<Vector512<short>>(ref values, inputStride, 1); |
|||
Vector512<short> input2 = Load<Vector512<short>>(ref values, inputStride, 2); |
|||
Vector512<short> input3 = Load<Vector512<short>>(ref values, inputStride, 3); |
|||
|
|||
// The paired stage keeps the axes in their native lane representation. Packed short lanes therefore retain
|
|||
// Highway's saturating add/subtract behavior before the widening butterfly multiplication.
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract(input0, input3, out buffer0[0], out buffer0[3]); |
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract(input1, input2, out buffer0[1], out buffer0[2]); |
|||
Av1ForwardTransformArithmetic<Vector512<short>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer0[0], |
|||
buffer0[1], |
|||
out Vector512<short> output0, |
|||
out Vector512<short> output2, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer0[3], |
|||
buffer0[2], |
|||
out Vector512<short> output1, |
|||
out Vector512<short> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<int>> buffer0, |
|||
ref Av1TransformVector<Vector128<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector128<int>>.CreateRounding(cosBit); |
|||
|
|||
Vector128<int> input0 = Load<Vector128<int>>(ref values, inputStride, 0); |
|||
Vector128<int> input1 = Load<Vector128<int>>(ref values, inputStride, 1); |
|||
Vector128<int> input2 = Load<Vector128<int>>(ref values, inputStride, 2); |
|||
Vector128<int> input3 = Load<Vector128<int>>(ref values, inputStride, 3); |
|||
|
|||
// The paired stage keeps the axes in their native lane representation. Packed short lanes therefore retain
|
|||
// Highway's saturating add/subtract behavior before the widening butterfly multiplication.
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract(input0, input3, out buffer0[0], out buffer0[3]); |
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract(input1, input2, out buffer0[1], out buffer0[2]); |
|||
Av1ForwardTransformArithmetic<Vector128<int>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer0[0], |
|||
buffer0[1], |
|||
out Vector128<int> output0, |
|||
out Vector128<int> output2, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer0[3], |
|||
buffer0[2], |
|||
out Vector128<int> output1, |
|||
out Vector128<int> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<int>> buffer0, |
|||
ref Av1TransformVector<Vector256<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector256<int>>.CreateRounding(cosBit); |
|||
|
|||
Vector256<int> input0 = Load<Vector256<int>>(ref values, inputStride, 0); |
|||
Vector256<int> input1 = Load<Vector256<int>>(ref values, inputStride, 1); |
|||
Vector256<int> input2 = Load<Vector256<int>>(ref values, inputStride, 2); |
|||
Vector256<int> input3 = Load<Vector256<int>>(ref values, inputStride, 3); |
|||
|
|||
// The paired stage keeps the axes in their native lane representation. Packed short lanes therefore retain
|
|||
// Highway's saturating add/subtract behavior before the widening butterfly multiplication.
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract(input0, input3, out buffer0[0], out buffer0[3]); |
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract(input1, input2, out buffer0[1], out buffer0[2]); |
|||
Av1ForwardTransformArithmetic<Vector256<int>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer0[0], |
|||
buffer0[1], |
|||
out Vector256<int> output0, |
|||
out Vector256<int> output2, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer0[3], |
|||
buffer0[2], |
|||
out Vector256<int> output1, |
|||
out Vector256<int> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<int>> buffer0, |
|||
ref Av1TransformVector<Vector512<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector512<int>>.CreateRounding(cosBit); |
|||
|
|||
Vector512<int> input0 = Load<Vector512<int>>(ref values, inputStride, 0); |
|||
Vector512<int> input1 = Load<Vector512<int>>(ref values, inputStride, 1); |
|||
Vector512<int> input2 = Load<Vector512<int>>(ref values, inputStride, 2); |
|||
Vector512<int> input3 = Load<Vector512<int>>(ref values, inputStride, 3); |
|||
|
|||
// The paired stage keeps the axes in their native lane representation. Packed short lanes therefore retain
|
|||
// Highway's saturating add/subtract behavior before the widening butterfly multiplication.
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract(input0, input3, out buffer0[0], out buffer0[3]); |
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract(input1, input2, out buffer0[1], out buffer0[2]); |
|||
Av1ForwardTransformArithmetic<Vector512<int>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer0[0], |
|||
buffer0[1], |
|||
out Vector512<int> output0, |
|||
out Vector512<int> output2, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer0[3], |
|||
buffer0[2], |
|||
out Vector512<int> output1, |
|||
out Vector512<int> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,859 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Implements the eight-point forward DCT stage network.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the eight-point forward transform for every supported lane width.
|
|||
/// </summary>
|
|||
internal readonly struct Dct8Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<int> buffer0, |
|||
ref Av1TransformVector<int> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<int>.CreateRounding(cosBit); |
|||
|
|||
// Stages 1 and 2 split the even and odd terms. The asymmetric destinations mirror Highway's buffer
|
|||
// ownership, allowing the later even butterflies to write their final coefficients directly to the block.
|
|||
Av1ForwardTransformArithmetic<int>.AddSubtract( |
|||
Load<int>(ref values, inputStride, 0), |
|||
Load<int>(ref values, inputStride, 7), |
|||
out buffer0[0], |
|||
out buffer1[7]); |
|||
|
|||
Av1ForwardTransformArithmetic<int>.AddSubtract( |
|||
Load<int>(ref values, inputStride, 1), |
|||
Load<int>(ref values, inputStride, 6), |
|||
out buffer0[1], |
|||
out buffer0[6]); |
|||
|
|||
Av1ForwardTransformArithmetic<int>.AddSubtract( |
|||
Load<int>(ref values, inputStride, 2), |
|||
Load<int>(ref values, inputStride, 5), |
|||
out buffer0[2], |
|||
out buffer0[5]); |
|||
|
|||
Av1ForwardTransformArithmetic<int>.AddSubtract( |
|||
Load<int>(ref values, inputStride, 3), |
|||
Load<int>(ref values, inputStride, 4), |
|||
out buffer0[3], |
|||
out buffer1[4]); |
|||
|
|||
Av1ForwardTransformArithmetic<int>.AddSubtract(buffer0[0], buffer0[3], out buffer1[0], out buffer1[3]); |
|||
Av1ForwardTransformArithmetic<int>.AddSubtract(buffer0[1], buffer0[2], out buffer1[1], out buffer1[2]); |
|||
Av1ForwardTransformArithmetic<int>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[5], |
|||
buffer0[6], |
|||
out buffer1[5], |
|||
out buffer1[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 completes the even half directly in coefficient order and prepares the four remaining odd terms.
|
|||
Av1ForwardTransformArithmetic<int>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer1[0], |
|||
buffer1[1], |
|||
out int output0, |
|||
out int output4, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<int>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer1[3], |
|||
buffer1[2], |
|||
out int output2, |
|||
out int output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<int>.AddSubtract(buffer1[4], buffer1[5], out buffer0[4], out buffer0[5]); |
|||
Av1ForwardTransformArithmetic<int>.AddSubtract(buffer1[7], buffer1[6], out buffer0[7], out buffer0[6]); |
|||
|
|||
// Highway fuses the final two stages because no intermediate value is reused after either rotation.
|
|||
Av1ForwardTransformArithmetic<int>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer0[7], |
|||
buffer0[4], |
|||
out int output1, |
|||
out int output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<int>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer0[6], |
|||
buffer0[5], |
|||
out int output5, |
|||
out int output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<short> buffer0, |
|||
ref Av1TransformVector<short> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<short>.CreateRounding(cosBit); |
|||
|
|||
// Stages 1 and 2 split the even and odd terms. The asymmetric destinations mirror Highway's buffer
|
|||
// ownership, allowing the later even butterflies to write their final coefficients directly to the block.
|
|||
Av1ForwardTransformArithmetic<short>.AddSubtract( |
|||
Load<short>(ref values, inputStride, 0), |
|||
Load<short>(ref values, inputStride, 7), |
|||
out buffer0[0], |
|||
out buffer1[7]); |
|||
|
|||
Av1ForwardTransformArithmetic<short>.AddSubtract( |
|||
Load<short>(ref values, inputStride, 1), |
|||
Load<short>(ref values, inputStride, 6), |
|||
out buffer0[1], |
|||
out buffer0[6]); |
|||
|
|||
Av1ForwardTransformArithmetic<short>.AddSubtract( |
|||
Load<short>(ref values, inputStride, 2), |
|||
Load<short>(ref values, inputStride, 5), |
|||
out buffer0[2], |
|||
out buffer0[5]); |
|||
|
|||
Av1ForwardTransformArithmetic<short>.AddSubtract( |
|||
Load<short>(ref values, inputStride, 3), |
|||
Load<short>(ref values, inputStride, 4), |
|||
out buffer0[3], |
|||
out buffer1[4]); |
|||
|
|||
Av1ForwardTransformArithmetic<short>.AddSubtract(buffer0[0], buffer0[3], out buffer1[0], out buffer1[3]); |
|||
Av1ForwardTransformArithmetic<short>.AddSubtract(buffer0[1], buffer0[2], out buffer1[1], out buffer1[2]); |
|||
Av1ForwardTransformArithmetic<short>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[5], |
|||
buffer0[6], |
|||
out buffer1[5], |
|||
out buffer1[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 completes the even half directly in coefficient order and prepares the four remaining odd terms.
|
|||
Av1ForwardTransformArithmetic<short>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer1[0], |
|||
buffer1[1], |
|||
out short output0, |
|||
out short output4, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<short>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer1[3], |
|||
buffer1[2], |
|||
out short output2, |
|||
out short output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<short>.AddSubtract(buffer1[4], buffer1[5], out buffer0[4], out buffer0[5]); |
|||
Av1ForwardTransformArithmetic<short>.AddSubtract(buffer1[7], buffer1[6], out buffer0[7], out buffer0[6]); |
|||
|
|||
// Highway fuses the final two stages because no intermediate value is reused after either rotation.
|
|||
Av1ForwardTransformArithmetic<short>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer0[7], |
|||
buffer0[4], |
|||
out short output1, |
|||
out short output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<short>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer0[6], |
|||
buffer0[5], |
|||
out short output5, |
|||
out short output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<short>> buffer0, |
|||
ref Av1TransformVector<Vector128<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector128<short>>.CreateRounding(cosBit); |
|||
|
|||
// Stages 1 and 2 split the even and odd terms. The asymmetric destinations mirror Highway's buffer
|
|||
// ownership, allowing the later even butterflies to write their final coefficients directly to the block.
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract( |
|||
Load<Vector128<short>>(ref values, inputStride, 0), |
|||
Load<Vector128<short>>(ref values, inputStride, 7), |
|||
out buffer0[0], |
|||
out buffer1[7]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract( |
|||
Load<Vector128<short>>(ref values, inputStride, 1), |
|||
Load<Vector128<short>>(ref values, inputStride, 6), |
|||
out buffer0[1], |
|||
out buffer0[6]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract( |
|||
Load<Vector128<short>>(ref values, inputStride, 2), |
|||
Load<Vector128<short>>(ref values, inputStride, 5), |
|||
out buffer0[2], |
|||
out buffer0[5]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract( |
|||
Load<Vector128<short>>(ref values, inputStride, 3), |
|||
Load<Vector128<short>>(ref values, inputStride, 4), |
|||
out buffer0[3], |
|||
out buffer1[4]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract(buffer0[0], buffer0[3], out buffer1[0], out buffer1[3]); |
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract(buffer0[1], buffer0[2], out buffer1[1], out buffer1[2]); |
|||
Av1ForwardTransformArithmetic<Vector128<short>>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[5], |
|||
buffer0[6], |
|||
out buffer1[5], |
|||
out buffer1[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 completes the even half directly in coefficient order and prepares the four remaining odd terms.
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer1[0], |
|||
buffer1[1], |
|||
out Vector128<short> output0, |
|||
out Vector128<short> output4, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer1[3], |
|||
buffer1[2], |
|||
out Vector128<short> output2, |
|||
out Vector128<short> output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract(buffer1[4], buffer1[5], out buffer0[4], out buffer0[5]); |
|||
Av1ForwardTransformArithmetic<Vector128<short>>.AddSubtract(buffer1[7], buffer1[6], out buffer0[7], out buffer0[6]); |
|||
|
|||
// Highway fuses the final two stages because no intermediate value is reused after either rotation.
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer0[7], |
|||
buffer0[4], |
|||
out Vector128<short> output1, |
|||
out Vector128<short> output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<short>>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer0[6], |
|||
buffer0[5], |
|||
out Vector128<short> output5, |
|||
out Vector128<short> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<short>> buffer0, |
|||
ref Av1TransformVector<Vector256<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector256<short>>.CreateRounding(cosBit); |
|||
|
|||
// Stages 1 and 2 split the even and odd terms. The asymmetric destinations mirror Highway's buffer
|
|||
// ownership, allowing the later even butterflies to write their final coefficients directly to the block.
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract( |
|||
Load<Vector256<short>>(ref values, inputStride, 0), |
|||
Load<Vector256<short>>(ref values, inputStride, 7), |
|||
out buffer0[0], |
|||
out buffer1[7]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract( |
|||
Load<Vector256<short>>(ref values, inputStride, 1), |
|||
Load<Vector256<short>>(ref values, inputStride, 6), |
|||
out buffer0[1], |
|||
out buffer0[6]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract( |
|||
Load<Vector256<short>>(ref values, inputStride, 2), |
|||
Load<Vector256<short>>(ref values, inputStride, 5), |
|||
out buffer0[2], |
|||
out buffer0[5]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract( |
|||
Load<Vector256<short>>(ref values, inputStride, 3), |
|||
Load<Vector256<short>>(ref values, inputStride, 4), |
|||
out buffer0[3], |
|||
out buffer1[4]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract(buffer0[0], buffer0[3], out buffer1[0], out buffer1[3]); |
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract(buffer0[1], buffer0[2], out buffer1[1], out buffer1[2]); |
|||
Av1ForwardTransformArithmetic<Vector256<short>>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[5], |
|||
buffer0[6], |
|||
out buffer1[5], |
|||
out buffer1[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 completes the even half directly in coefficient order and prepares the four remaining odd terms.
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer1[0], |
|||
buffer1[1], |
|||
out Vector256<short> output0, |
|||
out Vector256<short> output4, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer1[3], |
|||
buffer1[2], |
|||
out Vector256<short> output2, |
|||
out Vector256<short> output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract(buffer1[4], buffer1[5], out buffer0[4], out buffer0[5]); |
|||
Av1ForwardTransformArithmetic<Vector256<short>>.AddSubtract(buffer1[7], buffer1[6], out buffer0[7], out buffer0[6]); |
|||
|
|||
// Highway fuses the final two stages because no intermediate value is reused after either rotation.
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer0[7], |
|||
buffer0[4], |
|||
out Vector256<short> output1, |
|||
out Vector256<short> output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<short>>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer0[6], |
|||
buffer0[5], |
|||
out Vector256<short> output5, |
|||
out Vector256<short> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<short>> buffer0, |
|||
ref Av1TransformVector<Vector512<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector512<short>>.CreateRounding(cosBit); |
|||
|
|||
// Stages 1 and 2 split the even and odd terms. The asymmetric destinations mirror Highway's buffer
|
|||
// ownership, allowing the later even butterflies to write their final coefficients directly to the block.
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract( |
|||
Load<Vector512<short>>(ref values, inputStride, 0), |
|||
Load<Vector512<short>>(ref values, inputStride, 7), |
|||
out buffer0[0], |
|||
out buffer1[7]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract( |
|||
Load<Vector512<short>>(ref values, inputStride, 1), |
|||
Load<Vector512<short>>(ref values, inputStride, 6), |
|||
out buffer0[1], |
|||
out buffer0[6]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract( |
|||
Load<Vector512<short>>(ref values, inputStride, 2), |
|||
Load<Vector512<short>>(ref values, inputStride, 5), |
|||
out buffer0[2], |
|||
out buffer0[5]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract( |
|||
Load<Vector512<short>>(ref values, inputStride, 3), |
|||
Load<Vector512<short>>(ref values, inputStride, 4), |
|||
out buffer0[3], |
|||
out buffer1[4]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract(buffer0[0], buffer0[3], out buffer1[0], out buffer1[3]); |
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract(buffer0[1], buffer0[2], out buffer1[1], out buffer1[2]); |
|||
Av1ForwardTransformArithmetic<Vector512<short>>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[5], |
|||
buffer0[6], |
|||
out buffer1[5], |
|||
out buffer1[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 completes the even half directly in coefficient order and prepares the four remaining odd terms.
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer1[0], |
|||
buffer1[1], |
|||
out Vector512<short> output0, |
|||
out Vector512<short> output4, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer1[3], |
|||
buffer1[2], |
|||
out Vector512<short> output2, |
|||
out Vector512<short> output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract(buffer1[4], buffer1[5], out buffer0[4], out buffer0[5]); |
|||
Av1ForwardTransformArithmetic<Vector512<short>>.AddSubtract(buffer1[7], buffer1[6], out buffer0[7], out buffer0[6]); |
|||
|
|||
// Highway fuses the final two stages because no intermediate value is reused after either rotation.
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer0[7], |
|||
buffer0[4], |
|||
out Vector512<short> output1, |
|||
out Vector512<short> output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<short>>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer0[6], |
|||
buffer0[5], |
|||
out Vector512<short> output5, |
|||
out Vector512<short> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<int>> buffer0, |
|||
ref Av1TransformVector<Vector128<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector128<int>>.CreateRounding(cosBit); |
|||
|
|||
// Stages 1 and 2 split the even and odd terms. The asymmetric destinations mirror Highway's buffer
|
|||
// ownership, allowing the later even butterflies to write their final coefficients directly to the block.
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract( |
|||
Load<Vector128<int>>(ref values, inputStride, 0), |
|||
Load<Vector128<int>>(ref values, inputStride, 7), |
|||
out buffer0[0], |
|||
out buffer1[7]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract( |
|||
Load<Vector128<int>>(ref values, inputStride, 1), |
|||
Load<Vector128<int>>(ref values, inputStride, 6), |
|||
out buffer0[1], |
|||
out buffer0[6]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract( |
|||
Load<Vector128<int>>(ref values, inputStride, 2), |
|||
Load<Vector128<int>>(ref values, inputStride, 5), |
|||
out buffer0[2], |
|||
out buffer0[5]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract( |
|||
Load<Vector128<int>>(ref values, inputStride, 3), |
|||
Load<Vector128<int>>(ref values, inputStride, 4), |
|||
out buffer0[3], |
|||
out buffer1[4]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract(buffer0[0], buffer0[3], out buffer1[0], out buffer1[3]); |
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract(buffer0[1], buffer0[2], out buffer1[1], out buffer1[2]); |
|||
Av1ForwardTransformArithmetic<Vector128<int>>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[5], |
|||
buffer0[6], |
|||
out buffer1[5], |
|||
out buffer1[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 completes the even half directly in coefficient order and prepares the four remaining odd terms.
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer1[0], |
|||
buffer1[1], |
|||
out Vector128<int> output0, |
|||
out Vector128<int> output4, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer1[3], |
|||
buffer1[2], |
|||
out Vector128<int> output2, |
|||
out Vector128<int> output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract(buffer1[4], buffer1[5], out buffer0[4], out buffer0[5]); |
|||
Av1ForwardTransformArithmetic<Vector128<int>>.AddSubtract(buffer1[7], buffer1[6], out buffer0[7], out buffer0[6]); |
|||
|
|||
// Highway fuses the final two stages because no intermediate value is reused after either rotation.
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer0[7], |
|||
buffer0[4], |
|||
out Vector128<int> output1, |
|||
out Vector128<int> output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector128<int>>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer0[6], |
|||
buffer0[5], |
|||
out Vector128<int> output5, |
|||
out Vector128<int> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<int>> buffer0, |
|||
ref Av1TransformVector<Vector256<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector256<int>>.CreateRounding(cosBit); |
|||
|
|||
// Stages 1 and 2 split the even and odd terms. The asymmetric destinations mirror Highway's buffer
|
|||
// ownership, allowing the later even butterflies to write their final coefficients directly to the block.
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract( |
|||
Load<Vector256<int>>(ref values, inputStride, 0), |
|||
Load<Vector256<int>>(ref values, inputStride, 7), |
|||
out buffer0[0], |
|||
out buffer1[7]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract( |
|||
Load<Vector256<int>>(ref values, inputStride, 1), |
|||
Load<Vector256<int>>(ref values, inputStride, 6), |
|||
out buffer0[1], |
|||
out buffer0[6]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract( |
|||
Load<Vector256<int>>(ref values, inputStride, 2), |
|||
Load<Vector256<int>>(ref values, inputStride, 5), |
|||
out buffer0[2], |
|||
out buffer0[5]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract( |
|||
Load<Vector256<int>>(ref values, inputStride, 3), |
|||
Load<Vector256<int>>(ref values, inputStride, 4), |
|||
out buffer0[3], |
|||
out buffer1[4]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract(buffer0[0], buffer0[3], out buffer1[0], out buffer1[3]); |
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract(buffer0[1], buffer0[2], out buffer1[1], out buffer1[2]); |
|||
Av1ForwardTransformArithmetic<Vector256<int>>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[5], |
|||
buffer0[6], |
|||
out buffer1[5], |
|||
out buffer1[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 completes the even half directly in coefficient order and prepares the four remaining odd terms.
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer1[0], |
|||
buffer1[1], |
|||
out Vector256<int> output0, |
|||
out Vector256<int> output4, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer1[3], |
|||
buffer1[2], |
|||
out Vector256<int> output2, |
|||
out Vector256<int> output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract(buffer1[4], buffer1[5], out buffer0[4], out buffer0[5]); |
|||
Av1ForwardTransformArithmetic<Vector256<int>>.AddSubtract(buffer1[7], buffer1[6], out buffer0[7], out buffer0[6]); |
|||
|
|||
// Highway fuses the final two stages because no intermediate value is reused after either rotation.
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer0[7], |
|||
buffer0[4], |
|||
out Vector256<int> output1, |
|||
out Vector256<int> output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector256<int>>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer0[6], |
|||
buffer0[5], |
|||
out Vector256<int> output5, |
|||
out Vector256<int> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<int>> buffer0, |
|||
ref Av1TransformVector<Vector512<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
Av1TransformRounding rounding = Av1ForwardTransformArithmetic<Vector512<int>>.CreateRounding(cosBit); |
|||
|
|||
// Stages 1 and 2 split the even and odd terms. The asymmetric destinations mirror Highway's buffer
|
|||
// ownership, allowing the later even butterflies to write their final coefficients directly to the block.
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract( |
|||
Load<Vector512<int>>(ref values, inputStride, 0), |
|||
Load<Vector512<int>>(ref values, inputStride, 7), |
|||
out buffer0[0], |
|||
out buffer1[7]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract( |
|||
Load<Vector512<int>>(ref values, inputStride, 1), |
|||
Load<Vector512<int>>(ref values, inputStride, 6), |
|||
out buffer0[1], |
|||
out buffer0[6]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract( |
|||
Load<Vector512<int>>(ref values, inputStride, 2), |
|||
Load<Vector512<int>>(ref values, inputStride, 5), |
|||
out buffer0[2], |
|||
out buffer0[5]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract( |
|||
Load<Vector512<int>>(ref values, inputStride, 3), |
|||
Load<Vector512<int>>(ref values, inputStride, 4), |
|||
out buffer0[3], |
|||
out buffer1[4]); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract(buffer0[0], buffer0[3], out buffer1[0], out buffer1[3]); |
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract(buffer0[1], buffer0[2], out buffer1[1], out buffer1[2]); |
|||
Av1ForwardTransformArithmetic<Vector512<int>>.Butterfly( |
|||
-cospi[32], |
|||
cospi[32], |
|||
buffer0[5], |
|||
buffer0[6], |
|||
out buffer1[5], |
|||
out buffer1[6], |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
// Stage 3 completes the even half directly in coefficient order and prepares the four remaining odd terms.
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.Butterfly( |
|||
cospi[32], |
|||
cospi[32], |
|||
buffer1[0], |
|||
buffer1[1], |
|||
out Vector512<int> output0, |
|||
out Vector512<int> output4, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.Butterfly( |
|||
cospi[16], |
|||
cospi[48], |
|||
buffer1[3], |
|||
buffer1[2], |
|||
out Vector512<int> output2, |
|||
out Vector512<int> output6, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract(buffer1[4], buffer1[5], out buffer0[4], out buffer0[5]); |
|||
Av1ForwardTransformArithmetic<Vector512<int>>.AddSubtract(buffer1[7], buffer1[6], out buffer0[7], out buffer0[6]); |
|||
|
|||
// Highway fuses the final two stages because no intermediate value is reused after either rotation.
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.Butterfly( |
|||
cospi[8], |
|||
cospi[56], |
|||
buffer0[7], |
|||
buffer0[4], |
|||
out Vector512<int> output1, |
|||
out Vector512<int> output7, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Av1ForwardTransformArithmetic<Vector512<int>>.Butterfly( |
|||
cospi[40], |
|||
cospi[24], |
|||
buffer0[6], |
|||
buffer0[5], |
|||
out Vector512<int> output5, |
|||
out Vector512<int> output3, |
|||
cosBit, |
|||
in rounding); |
|||
|
|||
Store(ref values, outputStride, 0, output0); |
|||
Store(ref values, outputStride, 1, output1); |
|||
Store(ref values, outputStride, 2, output2); |
|||
Store(ref values, outputStride, 3, output3); |
|||
Store(ref values, outputStride, 4, output4); |
|||
Store(ref values, outputStride, 5, output5); |
|||
Store(ref values, outputStride, 6, output6); |
|||
Store(ref values, outputStride, 7, output7); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,235 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the sixteen-point forward identity operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the sixteen-point forward identity transform.
|
|||
/// </summary>
|
|||
internal readonly struct Identity16Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<int> buffer0, |
|||
ref Av1TransformVector<int> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
int input = Load<int>(ref values, inputStride, i); |
|||
int output = Av1ForwardTransformArithmetic<int>.MultiplyRound( |
|||
input, |
|||
2 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<short> buffer0, |
|||
ref Av1TransformVector<short> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
short input = Load<short>(ref values, inputStride, i); |
|||
short output = Av1ForwardTransformArithmetic<short>.MultiplyRound( |
|||
input, |
|||
2 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<short>> buffer0, |
|||
ref Av1TransformVector<Vector128<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
Vector128<short> input = Load<Vector128<short>>(ref values, inputStride, i); |
|||
Vector128<short> output = Av1ForwardTransformArithmetic<Vector128<short>>.MultiplyRound( |
|||
input, |
|||
2 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<short>> buffer0, |
|||
ref Av1TransformVector<Vector256<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
Vector256<short> input = Load<Vector256<short>>(ref values, inputStride, i); |
|||
Vector256<short> output = Av1ForwardTransformArithmetic<Vector256<short>>.MultiplyRound( |
|||
input, |
|||
2 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<short>> buffer0, |
|||
ref Av1TransformVector<Vector512<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
Vector512<short> input = Load<Vector512<short>>(ref values, inputStride, i); |
|||
Vector512<short> output = Av1ForwardTransformArithmetic<Vector512<short>>.MultiplyRound( |
|||
input, |
|||
2 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<int>> buffer0, |
|||
ref Av1TransformVector<Vector128<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
Vector128<int> input = Load<Vector128<int>>(ref values, inputStride, i); |
|||
Vector128<int> output = Av1ForwardTransformArithmetic<Vector128<int>>.MultiplyRound( |
|||
input, |
|||
2 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<int>> buffer0, |
|||
ref Av1TransformVector<Vector256<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
Vector256<int> input = Load<Vector256<int>>(ref values, inputStride, i); |
|||
Vector256<int> output = Av1ForwardTransformArithmetic<Vector256<int>>.MultiplyRound( |
|||
input, |
|||
2 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<int>> buffer0, |
|||
ref Av1TransformVector<Vector512<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 16; i++) |
|||
{ |
|||
Vector512<int> input = Load<Vector512<int>>(ref values, inputStride, i); |
|||
Vector512<int> output = Av1ForwardTransformArithmetic<Vector512<int>>.MultiplyRound( |
|||
input, |
|||
2 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,211 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the thirty-two-point forward identity operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the thirty-two-point forward identity transform.
|
|||
/// </summary>
|
|||
internal readonly struct Identity32Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<int> buffer0, |
|||
ref Av1TransformVector<int> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
int input = Load<int>(ref values, inputStride, i); |
|||
int output = Av1ForwardTransformArithmetic<int>.ShiftLeft(input, 2); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<short> buffer0, |
|||
ref Av1TransformVector<short> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
short input = Load<short>(ref values, inputStride, i); |
|||
short output = Av1ForwardTransformArithmetic<short>.ShiftLeft(input, 2); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<short>> buffer0, |
|||
ref Av1TransformVector<Vector128<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
Vector128<short> input = Load<Vector128<short>>(ref values, inputStride, i); |
|||
Vector128<short> output = Av1ForwardTransformArithmetic<Vector128<short>>.ShiftLeft(input, 2); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<short>> buffer0, |
|||
ref Av1TransformVector<Vector256<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
Vector256<short> input = Load<Vector256<short>>(ref values, inputStride, i); |
|||
Vector256<short> output = Av1ForwardTransformArithmetic<Vector256<short>>.ShiftLeft(input, 2); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<short>> buffer0, |
|||
ref Av1TransformVector<Vector512<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
Vector512<short> input = Load<Vector512<short>>(ref values, inputStride, i); |
|||
Vector512<short> output = Av1ForwardTransformArithmetic<Vector512<short>>.ShiftLeft(input, 2); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<int>> buffer0, |
|||
ref Av1TransformVector<Vector128<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
Vector128<int> input = Load<Vector128<int>>(ref values, inputStride, i); |
|||
Vector128<int> output = Av1ForwardTransformArithmetic<Vector128<int>>.ShiftLeft(input, 2); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<int>> buffer0, |
|||
ref Av1TransformVector<Vector256<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
Vector256<int> input = Load<Vector256<int>>(ref values, inputStride, i); |
|||
Vector256<int> output = Av1ForwardTransformArithmetic<Vector256<int>>.ShiftLeft(input, 2); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<int>> buffer0, |
|||
ref Av1TransformVector<Vector512<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 32; i++) |
|||
{ |
|||
Vector512<int> input = Load<Vector512<int>>(ref values, inputStride, i); |
|||
Vector512<int> output = Av1ForwardTransformArithmetic<Vector512<int>>.ShiftLeft(input, 2); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,235 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the four-point forward identity operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the four-point forward identity transform.
|
|||
/// </summary>
|
|||
internal readonly struct Identity4Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<int> buffer0, |
|||
ref Av1TransformVector<int> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
int input = Load<int>(ref values, inputStride, i); |
|||
int output = Av1ForwardTransformArithmetic<int>.MultiplyRound( |
|||
input, |
|||
1 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<short> buffer0, |
|||
ref Av1TransformVector<short> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
short input = Load<short>(ref values, inputStride, i); |
|||
short output = Av1ForwardTransformArithmetic<short>.MultiplyRound( |
|||
input, |
|||
1 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<short>> buffer0, |
|||
ref Av1TransformVector<Vector128<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Vector128<short> input = Load<Vector128<short>>(ref values, inputStride, i); |
|||
Vector128<short> output = Av1ForwardTransformArithmetic<Vector128<short>>.MultiplyRound( |
|||
input, |
|||
1 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<short>> buffer0, |
|||
ref Av1TransformVector<Vector256<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Vector256<short> input = Load<Vector256<short>>(ref values, inputStride, i); |
|||
Vector256<short> output = Av1ForwardTransformArithmetic<Vector256<short>>.MultiplyRound( |
|||
input, |
|||
1 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<short>> buffer0, |
|||
ref Av1TransformVector<Vector512<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Vector512<short> input = Load<Vector512<short>>(ref values, inputStride, i); |
|||
Vector512<short> output = Av1ForwardTransformArithmetic<Vector512<short>>.MultiplyRound( |
|||
input, |
|||
1 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<int>> buffer0, |
|||
ref Av1TransformVector<Vector128<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Vector128<int> input = Load<Vector128<int>>(ref values, inputStride, i); |
|||
Vector128<int> output = Av1ForwardTransformArithmetic<Vector128<int>>.MultiplyRound( |
|||
input, |
|||
1 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<int>> buffer0, |
|||
ref Av1TransformVector<Vector256<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Vector256<int> input = Load<Vector256<int>>(ref values, inputStride, i); |
|||
Vector256<int> output = Av1ForwardTransformArithmetic<Vector256<int>>.MultiplyRound( |
|||
input, |
|||
1 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<int>> buffer0, |
|||
ref Av1TransformVector<Vector512<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 4; i++) |
|||
{ |
|||
Vector512<int> input = Load<Vector512<int>>(ref values, inputStride, i); |
|||
Vector512<int> output = Av1ForwardTransformArithmetic<Vector512<int>>.MultiplyRound( |
|||
input, |
|||
1 * Av1Transform1dMath.NewSqrt2, |
|||
Av1Transform1dMath.NewSqrt2Bits); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,211 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the eight-point forward identity operator.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the eight-point forward identity transform.
|
|||
/// </summary>
|
|||
internal readonly struct Identity8Operator : IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<int> buffer0, |
|||
ref Av1TransformVector<int> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
int input = Load<int>(ref values, inputStride, i); |
|||
int output = Av1ForwardTransformArithmetic<int>.ShiftLeft(input, 1); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<short> buffer0, |
|||
ref Av1TransformVector<short> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
short input = Load<short>(ref values, inputStride, i); |
|||
short output = Av1ForwardTransformArithmetic<short>.ShiftLeft(input, 1); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<short>> buffer0, |
|||
ref Av1TransformVector<Vector128<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Vector128<short> input = Load<Vector128<short>>(ref values, inputStride, i); |
|||
Vector128<short> output = Av1ForwardTransformArithmetic<Vector128<short>>.ShiftLeft(input, 1); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<short>> buffer0, |
|||
ref Av1TransformVector<Vector256<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Vector256<short> input = Load<Vector256<short>>(ref values, inputStride, i); |
|||
Vector256<short> output = Av1ForwardTransformArithmetic<Vector256<short>>.ShiftLeft(input, 1); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<short>> buffer0, |
|||
ref Av1TransformVector<Vector512<short>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Vector512<short> input = Load<Vector512<short>>(ref values, inputStride, i); |
|||
Vector512<short> output = Av1ForwardTransformArithmetic<Vector512<short>>.ShiftLeft(input, 1); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<int>> buffer0, |
|||
ref Av1TransformVector<Vector128<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Vector128<int> input = Load<Vector128<int>>(ref values, inputStride, i); |
|||
Vector128<int> output = Av1ForwardTransformArithmetic<Vector128<int>>.ShiftLeft(input, 1); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<int>> buffer0, |
|||
ref Av1TransformVector<Vector256<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Vector256<int> input = Load<Vector256<int>>(ref values, inputStride, i); |
|||
Vector256<int> output = Av1ForwardTransformArithmetic<Vector256<int>>.ShiftLeft(input, 1); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public static void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<int>> buffer0, |
|||
ref Av1TransformVector<Vector512<int>> buffer1, |
|||
int cosBit) |
|||
{ |
|||
_ = buffer0; |
|||
_ = buffer1; |
|||
_ = cosBit; |
|||
|
|||
// The length-specific normalization is applied directly in the semantic operator so each scalar
|
|||
// or SIMD overload retains the exact AV1 identity-transform arithmetic without a forwarding layer.
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
Vector512<int> input = Load<Vector512<int>>(ref values, inputStride, i); |
|||
Vector512<int> output = Av1ForwardTransformArithmetic<Vector512<int>>.ShiftLeft(input, 1); |
|||
|
|||
Store(ref values, outputStride, i, output); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the operator contract for one-dimensional AV1 forward transforms.
|
|||
/// </content>
|
|||
internal static partial class Av1ForwardTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the scalar and SIMD arithmetic for one AV1 forward transform.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Every overload applies the same stage network to independent transform axes. The family traversal selects one
|
|||
/// concrete lane width, while the closed semantic operator lets the JIT resolve the static call before the stages.
|
|||
/// </remarks>
|
|||
internal interface IAv1ForwardTransform1dOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Transforms one expanded axis without hardware vectorization.
|
|||
/// </summary>
|
|||
/// <param name="values">The first value in the strided transform storage.</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 transform-stage workspace buffer.</param>
|
|||
/// <param name="buffer1">The second transform-stage workspace buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the transform constants.</param>
|
|||
public static abstract void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<int> buffer0, |
|||
ref Av1TransformVector<int> buffer1, |
|||
int cosBit); |
|||
|
|||
/// <summary>
|
|||
/// Transforms one packed axis without hardware vectorization.
|
|||
/// </summary>
|
|||
/// <param name="values">The first value in the strided transform storage.</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 transform-stage workspace buffer.</param>
|
|||
/// <param name="buffer1">The second transform-stage workspace buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the transform constants.</param>
|
|||
public static abstract void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<short> buffer0, |
|||
ref Av1TransformVector<short> buffer1, |
|||
int cosBit); |
|||
|
|||
/// <summary>
|
|||
/// Transforms eight packed axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="values">The first value in the strided transform storage.</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 transform-stage workspace buffer.</param>
|
|||
/// <param name="buffer1">The second transform-stage workspace buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the transform constants.</param>
|
|||
public static abstract void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<short>> buffer0, |
|||
ref Av1TransformVector<Vector128<short>> buffer1, |
|||
int cosBit); |
|||
|
|||
/// <summary>
|
|||
/// Transforms sixteen packed axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="values">The first value in the strided transform storage.</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 transform-stage workspace buffer.</param>
|
|||
/// <param name="buffer1">The second transform-stage workspace buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the transform constants.</param>
|
|||
public static abstract void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<short>> buffer0, |
|||
ref Av1TransformVector<Vector256<short>> buffer1, |
|||
int cosBit); |
|||
|
|||
/// <summary>
|
|||
/// Transforms thirty-two packed axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="values">The first value in the strided transform storage.</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 transform-stage workspace buffer.</param>
|
|||
/// <param name="buffer1">The second transform-stage workspace buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the transform constants.</param>
|
|||
public static abstract void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<short>> buffer0, |
|||
ref Av1TransformVector<Vector512<short>> buffer1, |
|||
int cosBit); |
|||
|
|||
/// <summary>
|
|||
/// Transforms four expanded axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="values">The first value in the strided transform storage.</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 transform-stage workspace buffer.</param>
|
|||
/// <param name="buffer1">The second transform-stage workspace buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the transform constants.</param>
|
|||
public static abstract void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector128<int>> buffer0, |
|||
ref Av1TransformVector<Vector128<int>> buffer1, |
|||
int cosBit); |
|||
|
|||
/// <summary>
|
|||
/// Transforms eight expanded axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="values">The first value in the strided transform storage.</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 transform-stage workspace buffer.</param>
|
|||
/// <param name="buffer1">The second transform-stage workspace buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the transform constants.</param>
|
|||
public static abstract void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector256<int>> buffer0, |
|||
ref Av1TransformVector<Vector256<int>> buffer1, |
|||
int cosBit); |
|||
|
|||
/// <summary>
|
|||
/// Transforms sixteen expanded axes in parallel.
|
|||
/// </summary>
|
|||
/// <param name="values">The first value in the strided transform storage.</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 transform-stage workspace buffer.</param>
|
|||
/// <param name="buffer1">The second transform-stage workspace buffer.</param>
|
|||
/// <param name="cosBit">The fixed-point precision of the transform constants.</param>
|
|||
public static abstract void Transform( |
|||
ref byte values, |
|||
nint inputStride, |
|||
nint outputStride, |
|||
ref Av1TransformVector<Vector512<int>> buffer0, |
|||
ref Av1TransformVector<Vector512<int>> buffer1, |
|||
int cosBit); |
|||
} |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,21 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,34 +0,0 @@ |
|||
// 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; |
|||
} |
|||
@ -1,46 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,59 +0,0 @@ |
|||
// 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); |
|||
} |
|||
@ -1,568 +0,0 @@ |
|||
// 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; |
|||
} |
|||
} |
|||
@ -1,168 +0,0 @@ |
|||
// 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); |
|||
} |
|||
} |
|||
@ -1,289 +0,0 @@ |
|||
// 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; |
|||
} |
|||
} |
|||
@ -1,475 +0,0 @@ |
|||
// 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
@ -1,112 +0,0 @@ |
|||
// 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
@ -1,232 +0,0 @@ |
|||
// 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); |
|||
} |
|||
} |
|||
@ -1,81 +0,0 @@ |
|||
// 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; |
|||
} |
|||
} |
|||
@ -1,65 +0,0 @@ |
|||
// 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; |
|||
} |
|||
} |
|||
@ -1,81 +0,0 @@ |
|||
// 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; |
|||
} |
|||
} |
|||
@ -1,65 +0,0 @@ |
|||
// 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; |
|||
} |
|||
} |
|||
@ -0,0 +1,571 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Adst16Operator : 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,145 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Adst4Operator : 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) |
|||
{ |
|||
bool widenedRound = stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount; |
|||
|
|||
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); |
|||
|
|||
_ = 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) |
|||
{ |
|||
bool widenedRound = stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount; |
|||
|
|||
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); |
|||
|
|||
_ = step; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,292 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Adst8Operator : 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,478 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Dct16Operator : 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,115 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Dct4Operator : 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,235 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Dct8Operator : 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,84 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Identity16Operator : 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,68 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Identity32Operator : 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,84 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Identity4Operator : 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,68 @@ |
|||
// 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 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 static partial class Av1Inverse2dTransformer |
|||
{ |
|||
internal readonly struct Identity8Operator : 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; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
/// <content>
|
|||
/// Defines the inverse-transform operator contract.
|
|||
/// </content>
|
|||
internal static partial class Av1Inverse2dTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the scalar and SIMD arithmetic for one AV1 one-dimensional inverse transform.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Each overload performs the same staged fixed-point transform. Vector fields identify coefficient positions,
|
|||
/// while vector lanes identify independent rows or columns.
|
|||
/// </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,64 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
internal static partial class HevcDeblockingFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Accesses four columns across a horizontal edge.
|
|||
/// </summary>
|
|||
private readonly struct HorizontalEdgeOperator : IEdgeOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector128<int> LoadVector(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int count) |
|||
{ |
|||
ref ushort source = ref picture.GetRowSpan(plane, y + distance)[x]; |
|||
if (count == 2) |
|||
{ |
|||
// The packed load used by full-width segments would read two samples beyond a subsampled edge.
|
|||
return Vector128.Create((int)source, Unsafe.Add(ref source, 1), 0, 0); |
|||
} |
|||
|
|||
Vector64<ushort> packed = Unsafe.As<ushort, Vector64<ushort>>(ref source); |
|||
return Vector128.WidenLower(Vector128.Create(packed, Vector64<ushort>.Zero)).AsInt32(); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreVector( |
|||
HevcPictureBuffer picture, |
|||
HevcPlane plane, |
|||
int x, |
|||
int y, |
|||
int distance, |
|||
Vector128<int> value, |
|||
int count) |
|||
{ |
|||
ref ushort destination = ref picture.GetRowSpan(plane, y + distance)[x]; |
|||
if (count == 4) |
|||
{ |
|||
Vector64<ushort> packed = Vector128.Narrow(value, Vector128<int>.Zero).AsUInt16().GetLower(); |
|||
Unsafe.As<ushort, Vector64<ushort>>(ref destination) = packed; |
|||
return; |
|||
} |
|||
|
|||
destination = (ushort)value.GetElement(0); |
|||
Unsafe.Add(ref destination, 1) = (ushort)value.GetElement(1); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int LoadScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index) |
|||
=> picture.GetRowSpan(plane, y + distance)[x + index]; |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index, int value) |
|||
=> picture.GetRowSpan(plane, y + distance)[x + index] = (ushort)value; |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
internal static partial class HevcDeblockingFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Defines orientation-specific access to the four samples running along one deblocking edge segment.
|
|||
/// </summary>
|
|||
private interface IEdgeOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Loads samples at one signed distance across the edge.
|
|||
/// </summary>
|
|||
/// <param name="picture">The reconstructed picture.</param>
|
|||
/// <param name="plane">The component plane.</param>
|
|||
/// <param name="x">The first Q-side sample X coordinate.</param>
|
|||
/// <param name="y">The first Q-side sample Y coordinate.</param>
|
|||
/// <param name="distance">The signed sample distance across the edge.</param>
|
|||
/// <param name="count">The number of valid low lanes to load.</param>
|
|||
/// <returns>The widened samples ordered along the edge.</returns>
|
|||
public static abstract Vector128<int> LoadVector(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int count); |
|||
|
|||
/// <summary>
|
|||
/// Stores four samples at one signed distance across the edge.
|
|||
/// </summary>
|
|||
/// <param name="picture">The reconstructed picture.</param>
|
|||
/// <param name="plane">The component plane.</param>
|
|||
/// <param name="x">The first Q-side sample X coordinate.</param>
|
|||
/// <param name="y">The first Q-side sample Y coordinate.</param>
|
|||
/// <param name="distance">The signed sample distance across the edge.</param>
|
|||
/// <param name="value">The four widened samples ordered along the edge.</param>
|
|||
/// <param name="count">The number of low lanes to store.</param>
|
|||
public static abstract void StoreVector( |
|||
HevcPictureBuffer picture, |
|||
HevcPlane plane, |
|||
int x, |
|||
int y, |
|||
int distance, |
|||
Vector128<int> value, |
|||
int count); |
|||
|
|||
/// <summary>
|
|||
/// Loads one scalar sample at a signed distance across and an offset along the edge.
|
|||
/// </summary>
|
|||
/// <param name="picture">The reconstructed picture.</param>
|
|||
/// <param name="plane">The component plane.</param>
|
|||
/// <param name="x">The first Q-side sample X coordinate.</param>
|
|||
/// <param name="y">The first Q-side sample Y coordinate.</param>
|
|||
/// <param name="distance">The signed sample distance across the edge.</param>
|
|||
/// <param name="index">The sample offset along the edge.</param>
|
|||
/// <returns>The selected sample.</returns>
|
|||
public static abstract int LoadScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index); |
|||
|
|||
/// <summary>
|
|||
/// Stores one scalar sample at a signed distance across and an offset along the edge.
|
|||
/// </summary>
|
|||
/// <param name="picture">The reconstructed picture.</param>
|
|||
/// <param name="plane">The component plane.</param>
|
|||
/// <param name="x">The first Q-side sample X coordinate.</param>
|
|||
/// <param name="y">The first Q-side sample Y coordinate.</param>
|
|||
/// <param name="distance">The signed sample distance across the edge.</param>
|
|||
/// <param name="index">The sample offset along the edge.</param>
|
|||
/// <param name="value">The filtered sample.</param>
|
|||
public static abstract void StoreScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index, int value); |
|||
} |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
internal static partial class HevcDeblockingFilter |
|||
{ |
|||
/// <summary>
|
|||
/// Accesses four rows across a vertical edge.
|
|||
/// </summary>
|
|||
private readonly struct VerticalEdgeOperator : IEdgeOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector128<int> LoadVector(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int count) |
|||
{ |
|||
if (count == 4) |
|||
{ |
|||
return Vector128.Create( |
|||
(int)picture.GetRowSpan(plane, y)[x + distance], |
|||
picture.GetRowSpan(plane, y + 1)[x + distance], |
|||
picture.GetRowSpan(plane, y + 2)[x + distance], |
|||
picture.GetRowSpan(plane, y + 3)[x + distance]); |
|||
} |
|||
|
|||
// Subsampled chroma edges contain two samples. Zeroing the unused lanes keeps the vector path within
|
|||
// the plane while allowing the shared kernel to operate on both valid samples in one instruction stream.
|
|||
return Vector128.Create( |
|||
(int)picture.GetRowSpan(plane, y)[x + distance], |
|||
picture.GetRowSpan(plane, y + 1)[x + distance], |
|||
0, |
|||
0); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreVector( |
|||
HevcPictureBuffer picture, |
|||
HevcPlane plane, |
|||
int x, |
|||
int y, |
|||
int distance, |
|||
Vector128<int> value, |
|||
int count) |
|||
{ |
|||
for (int index = 0; index < count; index++) |
|||
{ |
|||
picture.GetRowSpan(plane, y + index)[x + distance] = (ushort)value.GetElement(index); |
|||
} |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int LoadScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index) |
|||
=> picture.GetRowSpan(plane, y + index)[x + distance]; |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void StoreScalar(HevcPictureBuffer picture, HevcPlane plane, int x, int y, int distance, int index, int value) |
|||
=> picture.GetRowSpan(plane, y + index)[x + distance] = (ushort)value; |
|||
} |
|||
} |
|||
@ -0,0 +1,346 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Common.Helpers; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <content>
|
|||
/// Defines angular intra-prediction arithmetic.
|
|||
/// </content>
|
|||
internal static partial class HevcIntraPredictor |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the thirty-three directional intra-prediction modes.
|
|||
/// </summary>
|
|||
private readonly struct AngularOperator : IHevcIntraPredictionOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Predict( |
|||
ReadOnlySpan<ushort> top, |
|||
ReadOnlySpan<ushort> left, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int size, |
|||
int mode, |
|||
int bitDepth, |
|||
bool filterPredictionEdges, |
|||
Span<ushort> scratch) |
|||
{ |
|||
if (mode == VerticalMode) |
|||
{ |
|||
PredictVertical(top, left, destination, destinationStride, size, bitDepth, filterPredictionEdges); |
|||
return; |
|||
} |
|||
|
|||
if (mode == HorizontalMode) |
|||
{ |
|||
PredictHorizontal(top, left, destination, destinationStride, size, bitDepth, filterPredictionEdges); |
|||
return; |
|||
} |
|||
|
|||
bool vertical = mode >= FirstVerticalMode; |
|||
int angleMode = vertical ? mode - VerticalMode : HorizontalMode - mode; |
|||
int absoluteAngleMode = Math.Abs(angleMode); |
|||
int angle = PredictionAngles[absoluteAngleMode] * Math.Sign(angleMode); |
|||
ReadOnlySpan<ushort> main = vertical ? top : left; |
|||
ReadOnlySpan<ushort> side = vertical ? left : top; |
|||
Span<ushort> temporaryBlock = scratch[..(size * size)]; |
|||
Span<ushort> extendedReference = scratch.Slice(size * size, (4 * size) + 1); |
|||
int mainOrigin = 0; |
|||
|
|||
if (angle < 0) |
|||
{ |
|||
mainOrigin = size * 2; |
|||
main[..(size + 1)].CopyTo(extendedReference[mainOrigin..]); |
|||
int inverseAngle = InversePredictionAngles[absoluteAngleMode]; |
|||
int inverseAngleSum = 128; |
|||
int minimumIndex = (size * angle) >> 5; |
|||
for (int index = -1; index > minimumIndex; index--) |
|||
{ |
|||
inverseAngleSum += inverseAngle; |
|||
extendedReference[mainOrigin + index] = side[inverseAngleSum >> 8]; |
|||
} |
|||
|
|||
main = extendedReference; |
|||
} |
|||
|
|||
Span<ushort> prediction = vertical ? destination : temporaryBlock; |
|||
int predictionStride = vertical ? destinationStride : size; |
|||
PredictAngularRows(main, mainOrigin, prediction, predictionStride, size, angle); |
|||
if (!vertical) |
|||
{ |
|||
TransposeBlock(temporaryBlock, destination, destinationStride, size); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copies the top reference into every row and optionally filters the first column.
|
|||
/// </summary>
|
|||
/// <param name="top">The top reference samples.</param>
|
|||
/// <param name="left">The left reference samples.</param>
|
|||
/// <param name="destination">The destination block origin.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
/// <param name="bitDepth">The reconstructed component precision.</param>
|
|||
/// <param name="filterPredictionEdges">Whether the vertical luma edge filter applies.</param>
|
|||
private static void PredictVertical( |
|||
ReadOnlySpan<ushort> top, |
|||
ReadOnlySpan<ushort> left, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int size, |
|||
int bitDepth, |
|||
bool filterPredictionEdges) |
|||
{ |
|||
ReadOnlySpan<ushort> row = top.Slice(1, size); |
|||
int maximum = (1 << bitDepth) - 1; |
|||
for (int y = 0; y < size; y++) |
|||
{ |
|||
row.CopyTo(destination.Slice(y * destinationStride, size)); |
|||
if (filterPredictionEdges) |
|||
{ |
|||
int sample = destination[y * destinationStride] + ((left[y + 1] - left[0]) >> 1); |
|||
destination[y * destinationStride] = (ushort)Math.Clamp(sample, 0, maximum); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fills each row from its left reference and optionally filters the first row.
|
|||
/// </summary>
|
|||
/// <param name="top">The top reference samples.</param>
|
|||
/// <param name="left">The left reference samples.</param>
|
|||
/// <param name="destination">The destination block origin.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
/// <param name="bitDepth">The reconstructed component precision.</param>
|
|||
/// <param name="filterPredictionEdges">Whether the horizontal luma edge filter applies.</param>
|
|||
private static void PredictHorizontal( |
|||
ReadOnlySpan<ushort> top, |
|||
ReadOnlySpan<ushort> left, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int size, |
|||
int bitDepth, |
|||
bool filterPredictionEdges) |
|||
{ |
|||
for (int y = 0; y < size; y++) |
|||
{ |
|||
destination.Slice(y * destinationStride, size).Fill(left[y + 1]); |
|||
} |
|||
|
|||
if (!filterPredictionEdges) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
int maximum = (1 << bitDepth) - 1; |
|||
for (int x = 0; x < size; x++) |
|||
{ |
|||
int sample = destination[x] + ((top[x + 1] - top[0]) >> 1); |
|||
destination[x] = (ushort)Math.Clamp(sample, 0, maximum); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates a vertical-oriented angular block using contiguous SIMD interpolation within each row.
|
|||
/// </summary>
|
|||
/// <param name="main">The main reference beginning at logical index zero.</param>
|
|||
/// <param name="mainOrigin">The span index corresponding to logical reference index zero.</param>
|
|||
/// <param name="destination">The contiguous destination or transposition scratch block.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
/// <param name="angle">The signed prediction displacement in thirty-second-sample units.</param>
|
|||
private static void PredictAngularRows( |
|||
ReadOnlySpan<ushort> main, |
|||
int mainOrigin, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int size, |
|||
int angle) |
|||
{ |
|||
for (int y = 0, deltaPosition = angle; y < size; y++, deltaPosition += angle) |
|||
{ |
|||
int deltaInteger = deltaPosition >> 5; |
|||
int deltaFraction = deltaPosition & 31; |
|||
int sourceOffset = mainOrigin + deltaInteger + 1; |
|||
Span<ushort> row = destination.Slice(y * destinationStride, size); |
|||
if (deltaFraction == 0) |
|||
{ |
|||
main.Slice(sourceOffset, size).CopyTo(row); |
|||
} |
|||
else |
|||
{ |
|||
InterpolateAngularRow(main[sourceOffset..], row, deltaFraction); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Interpolates one angular prediction row between consecutive main-reference samples.
|
|||
/// </summary>
|
|||
/// <param name="source">The first main-reference sample for the row.</param>
|
|||
/// <param name="destination">The destination prediction row.</param>
|
|||
/// <param name="fraction">The right-hand weight with a denominator of thirty-two.</param>
|
|||
private static void InterpolateAngularRow(ReadOnlySpan<ushort> source, Span<ushort> destination, int fraction) |
|||
{ |
|||
ref ushort sourceBase = ref MemoryMarshal.GetReference(source); |
|||
ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); |
|||
uint leftWeight = (uint)(32 - fraction); |
|||
uint rightWeight = (uint)fraction; |
|||
int i = 0; |
|||
|
|||
// Adjacent source vectors overlap by one sample, aligning each left/right reference pair in the same lane.
|
|||
// Widening keeps the largest 12-bit Q5 weighted sum below the UInt32 limit before narrowing to sample storage.
|
|||
if (Vector512.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = destination.Length - Vector512<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector512<ushort>.Count) |
|||
{ |
|||
Vector512<ushort> left = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); |
|||
Vector512<ushort> right = Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); |
|||
(Vector512<uint> leftLow, Vector512<uint> leftHigh) = Vector512.Widen(left); |
|||
(Vector512<uint> rightLow, Vector512<uint> rightHigh) = Vector512.Widen(right); |
|||
Vector512<uint> low = ((leftLow * leftWeight) + (rightLow * rightWeight) + Vector512.Create(16U)) >> 5; |
|||
Vector512<uint> high = ((leftHigh * leftWeight) + (rightHigh * rightWeight) + Vector512.Create(16U)) >> 5; |
|||
Vector512.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); |
|||
} |
|||
} |
|||
|
|||
if (Vector256.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = destination.Length - Vector256<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector256<ushort>.Count) |
|||
{ |
|||
Vector256<ushort> left = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); |
|||
Vector256<ushort> right = Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); |
|||
(Vector256<uint> leftLow, Vector256<uint> leftHigh) = Vector256.Widen(left); |
|||
(Vector256<uint> rightLow, Vector256<uint> rightHigh) = Vector256.Widen(right); |
|||
Vector256<uint> low = ((leftLow * leftWeight) + (rightLow * rightWeight) + Vector256.Create(16U)) >> 5; |
|||
Vector256<uint> high = ((leftHigh * leftWeight) + (rightHigh * rightWeight) + Vector256.Create(16U)) >> 5; |
|||
Vector256.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); |
|||
} |
|||
} |
|||
|
|||
if (Vector128.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = destination.Length - Vector128<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector128<ushort>.Count) |
|||
{ |
|||
Vector128<ushort> left = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); |
|||
Vector128<ushort> right = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); |
|||
(Vector128<uint> leftLow, Vector128<uint> leftHigh) = Vector128.Widen(left); |
|||
(Vector128<uint> rightLow, Vector128<uint> rightHigh) = Vector128.Widen(right); |
|||
Vector128<uint> low = ((leftLow * leftWeight) + (rightLow * rightWeight) + Vector128.Create(16U)) >> 5; |
|||
Vector128<uint> high = ((leftHigh * leftWeight) + (rightHigh * rightWeight) + Vector128.Create(16U)) >> 5; |
|||
Vector128.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); |
|||
} |
|||
} |
|||
|
|||
for (; i < destination.Length; i++) |
|||
{ |
|||
Unsafe.Add(ref destinationBase, i) = (ushort)(((source[i] * leftWeight) + (source[i + 1] * rightWeight) + 16) >> 5); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Transposes a square horizontal prediction block into the reconstructed destination.
|
|||
/// </summary>
|
|||
/// <param name="source">The contiguous transposed prediction block.</param>
|
|||
/// <param name="destination">The destination block origin.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
private static void TransposeBlock(ReadOnlySpan<ushort> source, Span<ushort> destination, int destinationStride, int size) |
|||
{ |
|||
if (Vector128.IsHardwareAccelerated && size >= Vector128<ushort>.Count) |
|||
{ |
|||
for (int y = 0; y < size; y += Vector128<ushort>.Count) |
|||
{ |
|||
for (int x = 0; x < size; x += Vector128<ushort>.Count) |
|||
{ |
|||
Transpose8x8(source, destination, destinationStride, size, x, y); |
|||
} |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
for (int y = 0; y < size; y++) |
|||
{ |
|||
for (int x = 0; x < size; x++) |
|||
{ |
|||
destination[(x * destinationStride) + y] = source[(y * size) + x]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Transposes one eight-by-eight tile of 16-bit prediction samples.
|
|||
/// </summary>
|
|||
/// <param name="source">The contiguous source block.</param>
|
|||
/// <param name="destination">The destination block origin.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="sourceStride">The contiguous source row stride.</param>
|
|||
/// <param name="x">The tile X coordinate in the source block.</param>
|
|||
/// <param name="y">The tile Y coordinate in the source block.</param>
|
|||
private static void Transpose8x8( |
|||
ReadOnlySpan<ushort> source, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int sourceStride, |
|||
int x, |
|||
int y) |
|||
{ |
|||
ref ushort sourceBase = ref MemoryMarshal.GetReference(source); |
|||
ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); |
|||
Vector128<short> row0 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 0) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row1 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 1) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row2 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 2) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row3 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 3) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row4 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 4) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row5 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 5) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row6 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 6) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row7 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 7) * sourceStride) + x)).AsInt16(); |
|||
|
|||
// Three zip stages exchange one, two, then four 16-bit coordinates. The resulting vectors are the eight
|
|||
// source columns in row order, so each can be stored contiguously into one destination row.
|
|||
Vector128<short> pair0 = Vector128_.UnpackLow(row0, row1); |
|||
Vector128<short> pair1 = Vector128_.UnpackHigh(row0, row1); |
|||
Vector128<short> pair2 = Vector128_.UnpackLow(row2, row3); |
|||
Vector128<short> pair3 = Vector128_.UnpackHigh(row2, row3); |
|||
Vector128<short> pair4 = Vector128_.UnpackLow(row4, row5); |
|||
Vector128<short> pair5 = Vector128_.UnpackHigh(row4, row5); |
|||
Vector128<short> pair6 = Vector128_.UnpackLow(row6, row7); |
|||
Vector128<short> pair7 = Vector128_.UnpackHigh(row6, row7); |
|||
Vector128<int> quad0 = Vector128_.UnpackLow(pair0.AsInt32(), pair2.AsInt32()); |
|||
Vector128<int> quad1 = Vector128_.UnpackHigh(pair0.AsInt32(), pair2.AsInt32()); |
|||
Vector128<int> quad2 = Vector128_.UnpackLow(pair1.AsInt32(), pair3.AsInt32()); |
|||
Vector128<int> quad3 = Vector128_.UnpackHigh(pair1.AsInt32(), pair3.AsInt32()); |
|||
Vector128<int> quad4 = Vector128_.UnpackLow(pair4.AsInt32(), pair6.AsInt32()); |
|||
Vector128<int> quad5 = Vector128_.UnpackHigh(pair4.AsInt32(), pair6.AsInt32()); |
|||
Vector128<int> quad6 = Vector128_.UnpackLow(pair5.AsInt32(), pair7.AsInt32()); |
|||
Vector128<int> quad7 = Vector128_.UnpackHigh(pair5.AsInt32(), pair7.AsInt32()); |
|||
Vector128<ushort> column0 = Vector128_.UnpackLow(quad0.AsInt64(), quad4.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column1 = Vector128_.UnpackHigh(quad0.AsInt64(), quad4.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column2 = Vector128_.UnpackLow(quad1.AsInt64(), quad5.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column3 = Vector128_.UnpackHigh(quad1.AsInt64(), quad5.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column4 = Vector128_.UnpackLow(quad2.AsInt64(), quad6.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column5 = Vector128_.UnpackHigh(quad2.AsInt64(), quad6.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column6 = Vector128_.UnpackLow(quad3.AsInt64(), quad7.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column7 = Vector128_.UnpackHigh(quad3.AsInt64(), quad7.AsInt64()).AsUInt16(); |
|||
column0.StoreUnsafe(ref destinationBase, (nuint)(((x + 0) * destinationStride) + y)); |
|||
column1.StoreUnsafe(ref destinationBase, (nuint)(((x + 1) * destinationStride) + y)); |
|||
column2.StoreUnsafe(ref destinationBase, (nuint)(((x + 2) * destinationStride) + y)); |
|||
column3.StoreUnsafe(ref destinationBase, (nuint)(((x + 3) * destinationStride) + y)); |
|||
column4.StoreUnsafe(ref destinationBase, (nuint)(((x + 4) * destinationStride) + y)); |
|||
column5.StoreUnsafe(ref destinationBase, (nuint)(((x + 5) * destinationStride) + y)); |
|||
column6.StoreUnsafe(ref destinationBase, (nuint)(((x + 6) * destinationStride) + y)); |
|||
column7.StoreUnsafe(ref destinationBase, (nuint)(((x + 7) * destinationStride) + y)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,108 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <content>
|
|||
/// Defines DC intra-prediction arithmetic.
|
|||
/// </content>
|
|||
internal static partial class HevcIntraPredictor |
|||
{ |
|||
/// <summary>
|
|||
/// Implements DC prediction and its optional luma boundary filter.
|
|||
/// </summary>
|
|||
private readonly struct DcOperator : IHevcIntraPredictionOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void Predict( |
|||
ReadOnlySpan<ushort> top, |
|||
ReadOnlySpan<ushort> left, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int size, |
|||
int mode, |
|||
int bitDepth, |
|||
bool filterPredictionEdges, |
|||
Span<ushort> scratch) |
|||
{ |
|||
uint sum = SumSamples(top.Slice(1, size)) + SumSamples(left.Slice(1, size)); |
|||
ushort dc = (ushort)((sum + (uint)size) >> (BitOperations.Log2((uint)size) + 1)); |
|||
for (int y = 0; y < size; y++) |
|||
{ |
|||
destination.Slice(y * destinationStride, size).Fill(dc); |
|||
} |
|||
|
|||
if (!filterPredictionEdges) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
destination[0] = (ushort)((top[1] + left[1] + (2 * dc) + 2) >> 2); |
|||
for (int x = 1; x < size; x++) |
|||
{ |
|||
destination[x] = (ushort)((top[x + 1] + (3 * dc) + 2) >> 2); |
|||
} |
|||
|
|||
for (int y = 1; y < size; y++) |
|||
{ |
|||
destination[y * destinationStride] = (ushort)((left[y + 1] + (3 * dc) + 2) >> 2); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sums reconstructed reference samples without overflowing their 16-bit storage.
|
|||
/// </summary>
|
|||
/// <param name="samples">The samples to sum.</param>
|
|||
/// <returns>The exact unsigned sum.</returns>
|
|||
private static uint SumSamples(ReadOnlySpan<ushort> samples) |
|||
{ |
|||
ref ushort samplesBase = ref MemoryMarshal.GetReference(samples); |
|||
uint sum = 0; |
|||
int i = 0; |
|||
|
|||
// Widen before reduction because a complete 64-sample, 12-bit reference edge exceeds UInt16. The shared index
|
|||
// lets narrower vectors consume only the remainder from the widest available path.
|
|||
if (Vector512.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = samples.Length - Vector512<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector512<ushort>.Count) |
|||
{ |
|||
(Vector512<uint> low, Vector512<uint> high) = Vector512.Widen(Vector512.LoadUnsafe(ref samplesBase, (nuint)i)); |
|||
sum += Vector512.Sum(low) + Vector512.Sum(high); |
|||
} |
|||
} |
|||
|
|||
if (Vector256.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = samples.Length - Vector256<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector256<ushort>.Count) |
|||
{ |
|||
(Vector256<uint> low, Vector256<uint> high) = Vector256.Widen(Vector256.LoadUnsafe(ref samplesBase, (nuint)i)); |
|||
sum += Vector256.Sum(low) + Vector256.Sum(high); |
|||
} |
|||
} |
|||
|
|||
if (Vector128.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = samples.Length - Vector128<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector128<ushort>.Count) |
|||
{ |
|||
(Vector128<uint> low, Vector128<uint> high) = Vector128.Widen(Vector128.LoadUnsafe(ref samplesBase, (nuint)i)); |
|||
sum += Vector128.Sum(low) + Vector128.Sum(high); |
|||
} |
|||
} |
|||
|
|||
for (; i < samples.Length; i++) |
|||
{ |
|||
sum += Unsafe.Add(ref samplesBase, i); |
|||
} |
|||
|
|||
return sum; |
|||
} |
|||
} |
|||
} |
|||
@ -1,431 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using SixLabors.ImageSharp.Common.Helpers; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <content>
|
|||
/// Provides shared SIMD operations used by the closed prediction operators. Each lane represents one output column;
|
|||
/// planar and angular interpolation widen 16-bit references before their Q5 weighted sums, then narrow only after the
|
|||
/// normative rounding shift. Horizontal prediction reuses the vertical row kernel through a caller-owned contiguous
|
|||
/// block and an eight-by-eight transpose, keeping the arithmetic identical without gathering strided destination rows.
|
|||
/// </content>
|
|||
internal static partial class HevcIntraPredictor |
|||
{ |
|||
/// <summary>
|
|||
/// Calculates one 512-bit half of a planar prediction row.
|
|||
/// </summary>
|
|||
/// <param name="top">The top reference samples.</param>
|
|||
/// <param name="indices">The zero-based X coordinates.</param>
|
|||
/// <param name="left">The left reference sample for the row.</param>
|
|||
/// <param name="topRight">The top-right reference sample.</param>
|
|||
/// <param name="bottomLeft">The bottom-left reference sample.</param>
|
|||
/// <param name="topWeight">The top-reference weight.</param>
|
|||
/// <param name="bottomWeight">The bottom-left-reference weight.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
/// <param name="rounding">The division rounding constant.</param>
|
|||
/// <param name="shift">The division shift.</param>
|
|||
/// <returns>The predicted samples as widened lanes.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector512<uint> CalculatePlanarVector( |
|||
Vector512<uint> top, |
|||
Vector512<uint> indices, |
|||
uint left, |
|||
uint topRight, |
|||
uint bottomLeft, |
|||
uint topWeight, |
|||
uint bottomWeight, |
|||
uint size, |
|||
uint rounding, |
|||
int shift) |
|||
{ |
|||
Vector512<uint> horizontal = ((Vector512.Create(size - 1) - indices) * left) + ((indices + Vector512<uint>.One) * topRight); |
|||
Vector512<uint> vertical = (top * topWeight) + Vector512.Create(bottomLeft * bottomWeight); |
|||
return (horizontal + vertical + Vector512.Create(rounding)) >> shift; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates one 256-bit half of a planar prediction row.
|
|||
/// </summary>
|
|||
/// <param name="top">The top reference samples.</param>
|
|||
/// <param name="indices">The zero-based X coordinates.</param>
|
|||
/// <param name="left">The left reference sample for the row.</param>
|
|||
/// <param name="topRight">The top-right reference sample.</param>
|
|||
/// <param name="bottomLeft">The bottom-left reference sample.</param>
|
|||
/// <param name="topWeight">The top-reference weight.</param>
|
|||
/// <param name="bottomWeight">The bottom-left-reference weight.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
/// <param name="rounding">The division rounding constant.</param>
|
|||
/// <param name="shift">The division shift.</param>
|
|||
/// <returns>The predicted samples as widened lanes.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector256<uint> CalculatePlanarVector( |
|||
Vector256<uint> top, |
|||
Vector256<uint> indices, |
|||
uint left, |
|||
uint topRight, |
|||
uint bottomLeft, |
|||
uint topWeight, |
|||
uint bottomWeight, |
|||
uint size, |
|||
uint rounding, |
|||
int shift) |
|||
{ |
|||
Vector256<uint> horizontal = ((Vector256.Create(size - 1) - indices) * left) + ((indices + Vector256<uint>.One) * topRight); |
|||
Vector256<uint> vertical = (top * topWeight) + Vector256.Create(bottomLeft * bottomWeight); |
|||
return (horizontal + vertical + Vector256.Create(rounding)) >> shift; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates one 128-bit half of a planar prediction row.
|
|||
/// </summary>
|
|||
/// <param name="top">The top reference samples.</param>
|
|||
/// <param name="indices">The zero-based X coordinates.</param>
|
|||
/// <param name="left">The left reference sample for the row.</param>
|
|||
/// <param name="topRight">The top-right reference sample.</param>
|
|||
/// <param name="bottomLeft">The bottom-left reference sample.</param>
|
|||
/// <param name="topWeight">The top-reference weight.</param>
|
|||
/// <param name="bottomWeight">The bottom-left-reference weight.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
/// <param name="rounding">The division rounding constant.</param>
|
|||
/// <param name="shift">The division shift.</param>
|
|||
/// <returns>The predicted samples as widened lanes.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector128<uint> CalculatePlanarVector( |
|||
Vector128<uint> top, |
|||
Vector128<uint> indices, |
|||
uint left, |
|||
uint topRight, |
|||
uint bottomLeft, |
|||
uint topWeight, |
|||
uint bottomWeight, |
|||
uint size, |
|||
uint rounding, |
|||
int shift) |
|||
{ |
|||
Vector128<uint> horizontal = ((Vector128.Create(size - 1) - indices) * left) + ((indices + Vector128<uint>.One) * topRight); |
|||
Vector128<uint> vertical = (top * topWeight) + Vector128.Create(bottomLeft * bottomWeight); |
|||
return (horizontal + vertical + Vector128.Create(rounding)) >> shift; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Sums reconstructed reference samples without overflowing their 16-bit storage.
|
|||
/// </summary>
|
|||
/// <param name="samples">The samples to sum.</param>
|
|||
/// <returns>The exact unsigned sum.</returns>
|
|||
private static uint SumSamples(ReadOnlySpan<ushort> samples) |
|||
{ |
|||
ref ushort samplesBase = ref MemoryMarshal.GetReference(samples); |
|||
uint sum = 0; |
|||
int i = 0; |
|||
|
|||
// Widen before reduction because a complete 64-sample, 12-bit reference edge exceeds UInt16. The shared index
|
|||
// lets narrower vectors consume only the remainder from the widest available path.
|
|||
if (Vector512.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = samples.Length - Vector512<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector512<ushort>.Count) |
|||
{ |
|||
(Vector512<uint> low, Vector512<uint> high) = Vector512.Widen(Vector512.LoadUnsafe(ref samplesBase, (nuint)i)); |
|||
sum += Vector512.Sum(low) + Vector512.Sum(high); |
|||
} |
|||
} |
|||
|
|||
if (Vector256.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = samples.Length - Vector256<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector256<ushort>.Count) |
|||
{ |
|||
(Vector256<uint> low, Vector256<uint> high) = Vector256.Widen(Vector256.LoadUnsafe(ref samplesBase, (nuint)i)); |
|||
sum += Vector256.Sum(low) + Vector256.Sum(high); |
|||
} |
|||
} |
|||
|
|||
if (Vector128.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = samples.Length - Vector128<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector128<ushort>.Count) |
|||
{ |
|||
(Vector128<uint> low, Vector128<uint> high) = Vector128.Widen(Vector128.LoadUnsafe(ref samplesBase, (nuint)i)); |
|||
sum += Vector128.Sum(low) + Vector128.Sum(high); |
|||
} |
|||
} |
|||
|
|||
for (; i < samples.Length; i++) |
|||
{ |
|||
sum += Unsafe.Add(ref samplesBase, i); |
|||
} |
|||
|
|||
return sum; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Copies the top reference into every row and optionally filters the first column.
|
|||
/// </summary>
|
|||
/// <param name="top">The top reference samples.</param>
|
|||
/// <param name="left">The left reference samples.</param>
|
|||
/// <param name="destination">The destination block origin.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
/// <param name="bitDepth">The reconstructed component precision.</param>
|
|||
/// <param name="filterPredictionEdges">Whether the vertical luma edge filter applies.</param>
|
|||
private static void PredictVertical( |
|||
ReadOnlySpan<ushort> top, |
|||
ReadOnlySpan<ushort> left, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int size, |
|||
int bitDepth, |
|||
bool filterPredictionEdges) |
|||
{ |
|||
ReadOnlySpan<ushort> row = top.Slice(1, size); |
|||
int maximum = (1 << bitDepth) - 1; |
|||
for (int y = 0; y < size; y++) |
|||
{ |
|||
row.CopyTo(destination.Slice(y * destinationStride, size)); |
|||
if (filterPredictionEdges) |
|||
{ |
|||
int sample = destination[y * destinationStride] + ((left[y + 1] - left[0]) >> 1); |
|||
destination[y * destinationStride] = (ushort)Math.Clamp(sample, 0, maximum); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Fills each row from its left reference and optionally filters the first row.
|
|||
/// </summary>
|
|||
/// <param name="top">The top reference samples.</param>
|
|||
/// <param name="left">The left reference samples.</param>
|
|||
/// <param name="destination">The destination block origin.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
/// <param name="bitDepth">The reconstructed component precision.</param>
|
|||
/// <param name="filterPredictionEdges">Whether the horizontal luma edge filter applies.</param>
|
|||
private static void PredictHorizontal( |
|||
ReadOnlySpan<ushort> top, |
|||
ReadOnlySpan<ushort> left, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int size, |
|||
int bitDepth, |
|||
bool filterPredictionEdges) |
|||
{ |
|||
for (int y = 0; y < size; y++) |
|||
{ |
|||
destination.Slice(y * destinationStride, size).Fill(left[y + 1]); |
|||
} |
|||
|
|||
if (!filterPredictionEdges) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
int maximum = (1 << bitDepth) - 1; |
|||
for (int x = 0; x < size; x++) |
|||
{ |
|||
int sample = destination[x] + ((top[x + 1] - top[0]) >> 1); |
|||
destination[x] = (ushort)Math.Clamp(sample, 0, maximum); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Generates a vertical-oriented angular block using contiguous SIMD interpolation within each row.
|
|||
/// </summary>
|
|||
/// <param name="main">The main reference beginning at logical index zero.</param>
|
|||
/// <param name="mainOrigin">The span index corresponding to logical reference index zero.</param>
|
|||
/// <param name="destination">The contiguous destination or transposition scratch block.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
/// <param name="angle">The signed prediction displacement in thirty-second-sample units.</param>
|
|||
private static void PredictAngularRows( |
|||
ReadOnlySpan<ushort> main, |
|||
int mainOrigin, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int size, |
|||
int angle) |
|||
{ |
|||
for (int y = 0, deltaPosition = angle; y < size; y++, deltaPosition += angle) |
|||
{ |
|||
int deltaInteger = deltaPosition >> 5; |
|||
int deltaFraction = deltaPosition & 31; |
|||
int sourceOffset = mainOrigin + deltaInteger + 1; |
|||
Span<ushort> row = destination.Slice(y * destinationStride, size); |
|||
if (deltaFraction == 0) |
|||
{ |
|||
main.Slice(sourceOffset, size).CopyTo(row); |
|||
} |
|||
else |
|||
{ |
|||
InterpolateAngularRow(main[sourceOffset..], row, deltaFraction); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Interpolates one angular prediction row between consecutive main-reference samples.
|
|||
/// </summary>
|
|||
/// <param name="source">The first main-reference sample for the row.</param>
|
|||
/// <param name="destination">The destination prediction row.</param>
|
|||
/// <param name="fraction">The right-hand weight with a denominator of thirty-two.</param>
|
|||
private static void InterpolateAngularRow(ReadOnlySpan<ushort> source, Span<ushort> destination, int fraction) |
|||
{ |
|||
ref ushort sourceBase = ref MemoryMarshal.GetReference(source); |
|||
ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); |
|||
uint leftWeight = (uint)(32 - fraction); |
|||
uint rightWeight = (uint)fraction; |
|||
int i = 0; |
|||
|
|||
// Adjacent source vectors overlap by one sample, aligning each left/right reference pair in the same lane.
|
|||
// Widening keeps the largest 12-bit Q5 weighted sum below the UInt32 limit before narrowing to sample storage.
|
|||
if (Vector512.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = destination.Length - Vector512<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector512<ushort>.Count) |
|||
{ |
|||
Vector512<ushort> left = Vector512.LoadUnsafe(ref sourceBase, (nuint)i); |
|||
Vector512<ushort> right = Vector512.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); |
|||
(Vector512<uint> leftLow, Vector512<uint> leftHigh) = Vector512.Widen(left); |
|||
(Vector512<uint> rightLow, Vector512<uint> rightHigh) = Vector512.Widen(right); |
|||
Vector512<uint> low = ((leftLow * leftWeight) + (rightLow * rightWeight) + Vector512.Create(16U)) >> 5; |
|||
Vector512<uint> high = ((leftHigh * leftWeight) + (rightHigh * rightWeight) + Vector512.Create(16U)) >> 5; |
|||
Vector512.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); |
|||
} |
|||
} |
|||
|
|||
if (Vector256.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = destination.Length - Vector256<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector256<ushort>.Count) |
|||
{ |
|||
Vector256<ushort> left = Vector256.LoadUnsafe(ref sourceBase, (nuint)i); |
|||
Vector256<ushort> right = Vector256.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); |
|||
(Vector256<uint> leftLow, Vector256<uint> leftHigh) = Vector256.Widen(left); |
|||
(Vector256<uint> rightLow, Vector256<uint> rightHigh) = Vector256.Widen(right); |
|||
Vector256<uint> low = ((leftLow * leftWeight) + (rightLow * rightWeight) + Vector256.Create(16U)) >> 5; |
|||
Vector256<uint> high = ((leftHigh * leftWeight) + (rightHigh * rightWeight) + Vector256.Create(16U)) >> 5; |
|||
Vector256.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); |
|||
} |
|||
} |
|||
|
|||
if (Vector128.IsHardwareAccelerated) |
|||
{ |
|||
int oneVectorFromEnd = destination.Length - Vector128<ushort>.Count; |
|||
for (; i <= oneVectorFromEnd; i += Vector128<ushort>.Count) |
|||
{ |
|||
Vector128<ushort> left = Vector128.LoadUnsafe(ref sourceBase, (nuint)i); |
|||
Vector128<ushort> right = Vector128.LoadUnsafe(ref sourceBase, (nuint)(i + 1)); |
|||
(Vector128<uint> leftLow, Vector128<uint> leftHigh) = Vector128.Widen(left); |
|||
(Vector128<uint> rightLow, Vector128<uint> rightHigh) = Vector128.Widen(right); |
|||
Vector128<uint> low = ((leftLow * leftWeight) + (rightLow * rightWeight) + Vector128.Create(16U)) >> 5; |
|||
Vector128<uint> high = ((leftHigh * leftWeight) + (rightHigh * rightWeight) + Vector128.Create(16U)) >> 5; |
|||
Vector128.Narrow(low, high).StoreUnsafe(ref Unsafe.Add(ref destinationBase, i)); |
|||
} |
|||
} |
|||
|
|||
for (; i < destination.Length; i++) |
|||
{ |
|||
Unsafe.Add(ref destinationBase, i) = (ushort)(((source[i] * leftWeight) + (source[i + 1] * rightWeight) + 16) >> 5); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Transposes a square horizontal prediction block into the reconstructed destination.
|
|||
/// </summary>
|
|||
/// <param name="source">The contiguous transposed prediction block.</param>
|
|||
/// <param name="destination">The destination block origin.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="size">The square block side.</param>
|
|||
private static void TransposeBlock(ReadOnlySpan<ushort> source, Span<ushort> destination, int destinationStride, int size) |
|||
{ |
|||
if (Vector128.IsHardwareAccelerated && size >= Vector128<ushort>.Count) |
|||
{ |
|||
for (int y = 0; y < size; y += Vector128<ushort>.Count) |
|||
{ |
|||
for (int x = 0; x < size; x += Vector128<ushort>.Count) |
|||
{ |
|||
Transpose8x8(source, destination, destinationStride, size, x, y); |
|||
} |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
for (int y = 0; y < size; y++) |
|||
{ |
|||
for (int x = 0; x < size; x++) |
|||
{ |
|||
destination[(x * destinationStride) + y] = source[(y * size) + x]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Transposes one eight-by-eight tile of 16-bit prediction samples.
|
|||
/// </summary>
|
|||
/// <param name="source">The contiguous source block.</param>
|
|||
/// <param name="destination">The destination block origin.</param>
|
|||
/// <param name="destinationStride">The destination row stride.</param>
|
|||
/// <param name="sourceStride">The contiguous source row stride.</param>
|
|||
/// <param name="x">The tile X coordinate in the source block.</param>
|
|||
/// <param name="y">The tile Y coordinate in the source block.</param>
|
|||
private static void Transpose8x8( |
|||
ReadOnlySpan<ushort> source, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int sourceStride, |
|||
int x, |
|||
int y) |
|||
{ |
|||
ref ushort sourceBase = ref MemoryMarshal.GetReference(source); |
|||
ref ushort destinationBase = ref MemoryMarshal.GetReference(destination); |
|||
Vector128<short> row0 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 0) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row1 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 1) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row2 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 2) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row3 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 3) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row4 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 4) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row5 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 5) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row6 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 6) * sourceStride) + x)).AsInt16(); |
|||
Vector128<short> row7 = Vector128.LoadUnsafe(ref sourceBase, (nuint)(((y + 7) * sourceStride) + x)).AsInt16(); |
|||
|
|||
// Three zip stages exchange one, two, then four 16-bit coordinates. The resulting vectors are the eight
|
|||
// source columns in row order, so each can be stored contiguously into one destination row.
|
|||
Vector128<short> pair0 = Vector128_.UnpackLow(row0, row1); |
|||
Vector128<short> pair1 = Vector128_.UnpackHigh(row0, row1); |
|||
Vector128<short> pair2 = Vector128_.UnpackLow(row2, row3); |
|||
Vector128<short> pair3 = Vector128_.UnpackHigh(row2, row3); |
|||
Vector128<short> pair4 = Vector128_.UnpackLow(row4, row5); |
|||
Vector128<short> pair5 = Vector128_.UnpackHigh(row4, row5); |
|||
Vector128<short> pair6 = Vector128_.UnpackLow(row6, row7); |
|||
Vector128<short> pair7 = Vector128_.UnpackHigh(row6, row7); |
|||
Vector128<int> quad0 = Vector128_.UnpackLow(pair0.AsInt32(), pair2.AsInt32()); |
|||
Vector128<int> quad1 = Vector128_.UnpackHigh(pair0.AsInt32(), pair2.AsInt32()); |
|||
Vector128<int> quad2 = Vector128_.UnpackLow(pair1.AsInt32(), pair3.AsInt32()); |
|||
Vector128<int> quad3 = Vector128_.UnpackHigh(pair1.AsInt32(), pair3.AsInt32()); |
|||
Vector128<int> quad4 = Vector128_.UnpackLow(pair4.AsInt32(), pair6.AsInt32()); |
|||
Vector128<int> quad5 = Vector128_.UnpackHigh(pair4.AsInt32(), pair6.AsInt32()); |
|||
Vector128<int> quad6 = Vector128_.UnpackLow(pair5.AsInt32(), pair7.AsInt32()); |
|||
Vector128<int> quad7 = Vector128_.UnpackHigh(pair5.AsInt32(), pair7.AsInt32()); |
|||
Vector128<ushort> column0 = Vector128_.UnpackLow(quad0.AsInt64(), quad4.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column1 = Vector128_.UnpackHigh(quad0.AsInt64(), quad4.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column2 = Vector128_.UnpackLow(quad1.AsInt64(), quad5.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column3 = Vector128_.UnpackHigh(quad1.AsInt64(), quad5.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column4 = Vector128_.UnpackLow(quad2.AsInt64(), quad6.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column5 = Vector128_.UnpackHigh(quad2.AsInt64(), quad6.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column6 = Vector128_.UnpackLow(quad3.AsInt64(), quad7.AsInt64()).AsUInt16(); |
|||
Vector128<ushort> column7 = Vector128_.UnpackHigh(quad3.AsInt64(), quad7.AsInt64()).AsUInt16(); |
|||
column0.StoreUnsafe(ref destinationBase, (nuint)(((x + 0) * destinationStride) + y)); |
|||
column1.StoreUnsafe(ref destinationBase, (nuint)(((x + 1) * destinationStride) + y)); |
|||
column2.StoreUnsafe(ref destinationBase, (nuint)(((x + 2) * destinationStride) + y)); |
|||
column3.StoreUnsafe(ref destinationBase, (nuint)(((x + 3) * destinationStride) + y)); |
|||
column4.StoreUnsafe(ref destinationBase, (nuint)(((x + 4) * destinationStride) + y)); |
|||
column5.StoreUnsafe(ref destinationBase, (nuint)(((x + 5) * destinationStride) + y)); |
|||
column6.StoreUnsafe(ref destinationBase, (nuint)(((x + 6) * destinationStride) + y)); |
|||
column7.StoreUnsafe(ref destinationBase, (nuint)(((x + 7) * destinationStride) + y)); |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <content>
|
|||
/// Defines the HEVC intra-prediction operator contract.
|
|||
/// </content>
|
|||
internal static partial class HevcIntraPredictor |
|||
{ |
|||
/// <summary>
|
|||
/// Defines one closed intra-prediction operation selected by the decoded mode.
|
|||
/// </summary>
|
|||
private interface IHevcIntraPredictionOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Reconstructs one square prediction block.
|
|||
/// </summary>
|
|||
/// <param name="top">The top-left, top, and top-right reference samples.</param>
|
|||
/// <param name="left">The top-left, left, and below-left reference samples.</param>
|
|||
/// <param name="destination">The destination buffer beginning at the block origin.</param>
|
|||
/// <param name="destinationStride">The destination row stride in samples.</param>
|
|||
/// <param name="size">The square block side in samples.</param>
|
|||
/// <param name="mode">The decoded prediction mode.</param>
|
|||
/// <param name="bitDepth">The reconstructed component precision.</param>
|
|||
/// <param name="filterPredictionEdges">Whether the luma edge filter applies to the selected block.</param>
|
|||
/// <param name="scratch">The caller-owned block and extended-reference scratch space.</param>
|
|||
public static abstract void Predict( |
|||
ReadOnlySpan<ushort> top, |
|||
ReadOnlySpan<ushort> left, |
|||
Span<ushort> destination, |
|||
int destinationStride, |
|||
int size, |
|||
int mode, |
|||
int bitDepth, |
|||
bool filterPredictionEdges, |
|||
Span<ushort> scratch); |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <content>
|
|||
/// Defines the sixteen-point inverse discrete cosine transform operator.
|
|||
/// </content>
|
|||
internal static partial class HevcInverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the sixteen-point inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
private readonly struct DiscreteCosine16Operator : IHevcInverseTransformOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static int Size => 16; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static bool UsesButterfly => true; |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetCoefficient(int frequency, int position) |
|||
{ |
|||
if (frequency == 0) |
|||
{ |
|||
return 64; |
|||
} |
|||
|
|||
int angle = ((2 * position) + 1) * frequency * 2; |
|||
angle &= 127; |
|||
if (angle > 64) |
|||
{ |
|||
angle = 128 - angle; |
|||
} |
|||
|
|||
// The second quadrant reuses the first-quadrant magnitude with a negative sign.
|
|||
if (angle > 32) |
|||
{ |
|||
return -DiscreteCosineMagnitudes[64 - angle]; |
|||
} |
|||
|
|||
return DiscreteCosineMagnitudes[angle]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <content>
|
|||
/// Defines the thirty-two-point inverse discrete cosine transform operator.
|
|||
/// </content>
|
|||
internal static partial class HevcInverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the thirty-two-point inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
private readonly struct DiscreteCosine32Operator : IHevcInverseTransformOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static int Size => 32; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static bool UsesButterfly => true; |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetCoefficient(int frequency, int position) |
|||
{ |
|||
if (frequency == 0) |
|||
{ |
|||
return 64; |
|||
} |
|||
|
|||
int angle = ((2 * position) + 1) * frequency * 1; |
|||
angle &= 127; |
|||
if (angle > 64) |
|||
{ |
|||
angle = 128 - angle; |
|||
} |
|||
|
|||
// The second quadrant reuses the first-quadrant magnitude with a negative sign.
|
|||
if (angle > 32) |
|||
{ |
|||
return -DiscreteCosineMagnitudes[64 - angle]; |
|||
} |
|||
|
|||
return DiscreteCosineMagnitudes[angle]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <content>
|
|||
/// Defines the four-point inverse discrete cosine transform operator.
|
|||
/// </content>
|
|||
internal static partial class HevcInverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the four-point inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
private readonly struct DiscreteCosine4Operator : IHevcInverseTransformOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static int Size => 4; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static bool UsesButterfly => true; |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetCoefficient(int frequency, int position) |
|||
{ |
|||
if (frequency == 0) |
|||
{ |
|||
return 64; |
|||
} |
|||
|
|||
int angle = ((2 * position) + 1) * frequency * 8; |
|||
angle &= 127; |
|||
if (angle > 64) |
|||
{ |
|||
angle = 128 - angle; |
|||
} |
|||
|
|||
// The second quadrant reuses the first-quadrant magnitude with a negative sign.
|
|||
if (angle > 32) |
|||
{ |
|||
return -DiscreteCosineMagnitudes[64 - angle]; |
|||
} |
|||
|
|||
return DiscreteCosineMagnitudes[angle]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <content>
|
|||
/// Defines the eight-point inverse discrete cosine transform operator.
|
|||
/// </content>
|
|||
internal static partial class HevcInverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the eight-point inverse discrete cosine transform.
|
|||
/// </summary>
|
|||
private readonly struct DiscreteCosine8Operator : IHevcInverseTransformOperator |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static int Size => 8; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static bool UsesButterfly => true; |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetCoefficient(int frequency, int position) |
|||
{ |
|||
if (frequency == 0) |
|||
{ |
|||
return 64; |
|||
} |
|||
|
|||
int angle = ((2 * position) + 1) * frequency * 4; |
|||
angle &= 127; |
|||
if (angle > 64) |
|||
{ |
|||
angle = 128 - angle; |
|||
} |
|||
|
|||
// The second quadrant reuses the first-quadrant magnitude with a negative sign.
|
|||
if (angle > 32) |
|||
{ |
|||
return -DiscreteCosineMagnitudes[64 - angle]; |
|||
} |
|||
|
|||
return DiscreteCosineMagnitudes[angle]; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
|||
|
|||
/// <content>
|
|||
/// Defines the four-point inverse discrete sine transform operator.
|
|||
/// </content>
|
|||
internal static partial class HevcInverseTransformer |
|||
{ |
|||
/// <summary>
|
|||
/// Implements the four-point inverse discrete sine transform.
|
|||
/// </summary>
|
|||
private readonly struct DiscreteSine4Operator : IHevcInverseTransformOperator |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the inverse-DST matrix in frequency-major order.
|
|||
/// </summary>
|
|||
private static ReadOnlySpan<sbyte> Coefficients => |
|||
[ |
|||
29, 55, 74, 84, |
|||
74, 74, 0, -74, |
|||
84, -29, -74, 55, |
|||
55, -84, 74, -29 |
|||
]; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static int Size => 4; |
|||
|
|||
/// <inheritdoc/>
|
|||
public static bool UsesButterfly => false; |
|||
|
|||
/// <inheritdoc/>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetCoefficient(int frequency, int position) |
|||
{ |
|||
return Coefficients[(frequency * Size) + position]; |
|||
} |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue