Browse Source

Restore AV1 transform operator architecture

pull/2633/head
James Jackson-South 4 days ago
parent
commit
86d44f158a
  1. 2
      HEIF_IMPLEMENTATION_PLAN.md
  2. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Adst16Operator.cs
  3. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Adst4Operator.cs
  4. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Adst8Operator.cs
  5. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct16Operator.cs
  6. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct32Operator.cs
  7. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct4Operator.cs
  8. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct64Operator.cs
  9. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct8Operator.cs
  10. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Identity16Operator.cs
  11. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Identity32Operator.cs
  12. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Identity4Operator.cs
  13. 29
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Identity8Operator.cs
  14. 80
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs
  15. 171
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs
  16. 94
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformOutputOperator.cs
  17. 574
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Adst16Operator.cs
  18. 174
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Adst4Operator.cs
  19. 295
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Adst8Operator.cs
  20. 481
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct16Operator.cs
  21. 1033
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct32Operator.cs
  22. 118
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct4Operator.cs
  23. 2278
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct64Operator.cs
  24. 238
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct8Operator.cs
  25. 87
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Identity16Operator.cs
  26. 71
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Identity32Operator.cs
  27. 87
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Identity4Operator.cs
  28. 71
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Identity8Operator.cs
  29. 100
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.OutputOperator.cs
  30. 2
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.cs
  31. 4
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformerFactory.cs
  32. 12
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseWalshHadamardTransformer.cs
  33. 5
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform1dMath.cs
  34. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst16Forward1dOperator.cs
  35. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst4Forward1dOperator.cs
  36. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst8Forward1dOperator.cs
  37. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct16Forward1dOperator.cs
  38. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct32Forward1dOperator.cs
  39. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct4Forward1dOperator.cs
  40. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct64Forward1dOperator.cs
  41. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct8Forward1dOperator.cs
  42. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity16Forward1dOperator.cs
  43. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity32Forward1dOperator.cs
  44. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity4Forward1dOperator.cs
  45. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity8Forward1dOperator.cs
  46. 34
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/IAv1ForwardTransform1dOperator.cs
  47. 46
      src/ImageSharp/Formats/Heif/Av1/Transform/IAv1InverseTransformOutputOperator.cs
  48. 59
      src/ImageSharp/Formats/Heif/Av1/Transform/IAv1Transform1dOperator.cs
  49. 568
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst16Inverse1dOperator.cs
  50. 168
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dOperator.cs
  51. 289
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst8Inverse1dOperator.cs
  52. 475
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct16Inverse1dOperator.cs
  53. 1027
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct32Inverse1dOperator.cs
  54. 112
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dOperator.cs
  55. 2272
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct64Inverse1dOperator.cs
  56. 232
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct8Inverse1dOperator.cs
  57. 81
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dOperator.cs
  58. 65
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dOperator.cs
  59. 81
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dOperator.cs
  60. 65
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dOperator.cs
  61. 111
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ForwardTransformTests.cs
  62. 140
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1InverseTransformTests.cs

2
HEIF_IMPLEMENTATION_PLAN.md

@ -856,7 +856,7 @@ No valid HEVC or AV1 color, compression, or bit-depth row may remain `unsupporte
- Keep changes vertical and reviewable. A slice should add one behavior, its focused tests, independent evidence, and any required notice update.
- Keep every AV1 prediction family on the established JPEG color-converter operator architecture. Each distinct traversal contract owns a family-named predictor type; its `.Operator.cs` defines the static interface, and its family-named files own the closed generic widest-to-narrowest SIMD traversal. Semantic `readonly struct` operators implement scalar, `Vector128`, `Vector256`, and `Vector512` arithmetic through that contract. Only modes which share the same traversal and contract may share a predictor family; do not nest a separate predictor beneath a broad intra/inter family or create hardware-width-specific class hierarchies.
- Keep AV1 forward and inverse transform operators under their family-named `Av1ForwardTransformer` and `Av1InverseTransformer` owners, matching the JPEG color-converter operator architecture. Their `.Operator.cs` files own the static contracts and generic dispatch, while the family-named operator files own each DCT, ADST, identity, and reconstruction-output implementation. Do not nest one-dimensional transform operators beneath a two-dimensional helper or name operator files after an implementation-detail dispatcher.
- Keep AV1 one-dimensional transform implementations as top-level semantic `readonly struct` operators. Forward operators belong to `Transform.Forward`, inverse operators belong to `Transform.Inverse`, and each family implements its top-level static-generic contract. Keep `Av1ForwardTransformer` and `Av1Inverse2dTransformer` as separate two-dimensional dispatchers, and keep `Av1InverseTransformOutputOperator<TSample>` top-level. Do not wrap or nest transform operators beneath either dispatcher and do not name operator files after an implementation-detail owner.
- Design SIMD-suitable codec work SIMD-first. Establish vector-friendly storage, operator boundaries, scratch ownership, traversal, every applicable lane width, and benchmark-gated dispatch before implementing the equivalent scalar fallback; never build a scalar production architecture and bolt SIMD onto it later.
- Inspect every owning method and upstream invariant before adding guards. Validate external file data at the parser/model boundary and rely on those established invariants internally.
- Do not extract one-use helpers merely to label code. Extract shared primitives only when they have genuine reuse or remove substantial complexity.

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Adst16Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the sixteen-point asymmetric discrete sine forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the sixteen-point AV1 forward asymmetric discrete sine transform operator.
/// </summary>
internal readonly struct Adst16Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Adst16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Adst4Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the four-point asymmetric discrete sine forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the four-point AV1 forward asymmetric discrete sine transform operator.
/// </summary>
internal readonly struct Adst4Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Adst4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Adst8Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the eight-point asymmetric discrete sine forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the eight-point AV1 forward asymmetric discrete sine transform operator.
/// </summary>
internal readonly struct Adst8Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Adst8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct16Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the sixteen-point discrete cosine forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the sixteen-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Dct16Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct32Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the thirty-two-point discrete cosine forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the thirty-two-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Dct32Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct32(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct4Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the four-point discrete cosine forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the four-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Dct4Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct64Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the sixty-four-point discrete cosine forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the sixty-four-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Dct64Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct64(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Dct8Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the eight-point discrete cosine forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the eight-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Dct8Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Identity16Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the sixteen-point identity forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the sixteen-point AV1 forward identity transform operator.
/// </summary>
internal readonly struct Identity16Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Identity16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Identity32Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the thirty-two-point identity forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the thirty-two-point AV1 forward identity transform operator.
/// </summary>
internal readonly struct Identity32Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Identity32(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Identity4Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the four-point identity forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the four-point AV1 forward identity transform operator.
/// </summary>
internal readonly struct Identity4Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Identity4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

29
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Identity8Operator.cs

@ -1,29 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the eight-point identity forward transform operator.
/// </content>
internal static partial class Av1ForwardTransformer
{
/// <summary>
/// Defines the eight-point AV1 forward identity transform operator.
/// </summary>
internal readonly struct Identity8Operator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Identity8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}
}

80
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.Operator.cs → src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs

@ -18,38 +18,8 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// Eight-bit blocks use saturating 16-bit stages where their normative ranges permit it; high-bit-depth and scalar
/// fallback paths retain 32-bit stages. Both representations produce the same row-major coefficient contract.
/// </remarks>
internal static partial class Av1ForwardTransformer
internal static class Av1ForwardTransformer
{
/// <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;
}
/// <summary>
/// Resolves and applies the configured two-dimensional AV1 forward transform.
/// </summary>
@ -95,40 +65,40 @@ internal static partial class Av1ForwardTransformer
switch (config.TransformFunctionTypeColumn)
{
case Av1TransformFunctionType.Dct4:
DispatchRow<Dct4Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Dct4Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct8:
DispatchRow<Dct8Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Dct8Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct16:
DispatchRow<Dct16Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Dct16Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct32:
DispatchRow<Dct32Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Dct32Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct64:
DispatchRow<Dct64Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Dct64Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst4:
DispatchRow<Adst4Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Adst4Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst8:
DispatchRow<Adst8Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Adst8Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst16:
DispatchRow<Adst16Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Adst16Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity4:
DispatchRow<Identity4Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Identity4Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity8:
DispatchRow<Identity8Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Identity8Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity16:
DispatchRow<Identity16Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Identity16Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity32:
DispatchRow<Identity32Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
DispatchRow<Av1Identity32Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
default:
throw new InvalidImageContentException($"The {config.TransformFunctionTypeColumn} column transform is not valid for {config.TransformSize}.");
@ -157,40 +127,40 @@ internal static partial class Av1ForwardTransformer
switch (config.TransformFunctionTypeRow)
{
case Av1TransformFunctionType.Dct4:
Transform2d<TColumnOperator, Dct4Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Dct4Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct8:
Transform2d<TColumnOperator, Dct8Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Dct8Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct16:
Transform2d<TColumnOperator, Dct16Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Dct16Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct32:
Transform2d<TColumnOperator, Dct32Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Dct32Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Dct64:
Transform2d<TColumnOperator, Dct64Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Dct64Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst4:
Transform2d<TColumnOperator, Adst4Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Adst4Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst8:
Transform2d<TColumnOperator, Adst8Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Adst8Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Adst16:
Transform2d<TColumnOperator, Adst16Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Adst16Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity4:
Transform2d<TColumnOperator, Identity4Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Identity4Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity8:
Transform2d<TColumnOperator, Identity8Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Identity8Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity16:
Transform2d<TColumnOperator, Identity16Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Identity16Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
case Av1TransformFunctionType.Identity32:
Transform2d<TColumnOperator, Identity32Operator>(input, coefficients, stride, bitDepth, ref config, workspace);
Transform2d<TColumnOperator, Av1Identity32Forward1dOperator>(input, coefficients, stride, bitDepth, ref config, workspace);
break;
default:
throw new InvalidImageContentException($"The {config.TransformFunctionTypeRow} row transform is not valid for {config.TransformSize}.");

171
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Operator.cs → src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs

@ -4,6 +4,7 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
@ -16,106 +17,8 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// cross-lane permutations. Reconstruction adds the final residuals to their matching prediction lanes before
/// narrowing to the decoded sample depth.
/// </remarks>
internal static partial class Av1InverseTransformer
internal static class Av1Inverse2dTransformer
{
/// <summary>
/// The signed stage width whose fixed-point terminal operations require widened SIMD intermediates.
/// </summary>
private const byte WidenedIntermediateBitCount = 20;
/// <summary>
/// Defines the scalar and SIMD arithmetic for one AV1 one-dimensional inverse transform.
/// </summary>
/// <remarks>
/// Each overload performs the same staged fixed-point inverse 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 IAv1InverseTransform1dOperator
{
/// <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);
}
/// <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);
}
/// <summary>
/// Applies an inverse transform and adds its residual to high-bit-depth predicted samples.
/// </summary>
@ -136,7 +39,7 @@ internal static partial class Av1InverseTransformer
ref Av1Transform2dFlipConfiguration config,
Span<int> workspace,
int bitDepth)
=> Transform2dAdd<short, OutputOperator<short>>(
=> Transform2dAdd<short, Av1InverseTransformOutputOperator<short>>(
input,
outputForRead,
strideForRead,
@ -164,7 +67,7 @@ internal static partial class Av1InverseTransformer
int strideForWrite,
ref Av1Transform2dFlipConfiguration config,
Span<int> workspace)
=> Transform2dAdd<byte, OutputOperator<byte>>(
=> Transform2dAdd<byte, Av1InverseTransformOutputOperator<byte>>(
input,
outputForRead,
strideForRead,
@ -193,62 +96,62 @@ internal static partial class Av1InverseTransformer
switch (config.TransformFunctionTypeColumn)
{
case Av1TransformFunctionType.Dct4:
DispatchRow<TSample, TOutputOperator, Dct4Operator>(
DispatchRow<TSample, TOutputOperator, Av1Dct4Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Dct8:
DispatchRow<TSample, TOutputOperator, Dct8Operator>(
DispatchRow<TSample, TOutputOperator, Av1Dct8Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Dct16:
DispatchRow<TSample, TOutputOperator, Dct16Operator>(
DispatchRow<TSample, TOutputOperator, Av1Dct16Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Dct32:
DispatchRow<TSample, TOutputOperator, Dct32Operator>(
DispatchRow<TSample, TOutputOperator, Av1Dct32Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Dct64:
DispatchRow<TSample, TOutputOperator, Dct64Operator>(
DispatchRow<TSample, TOutputOperator, Av1Dct64Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Adst4:
DispatchRow<TSample, TOutputOperator, Adst4Operator>(
DispatchRow<TSample, TOutputOperator, Av1Adst4Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Adst8:
DispatchRow<TSample, TOutputOperator, Adst8Operator>(
DispatchRow<TSample, TOutputOperator, Av1Adst8Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Adst16:
DispatchRow<TSample, TOutputOperator, Adst16Operator>(
DispatchRow<TSample, TOutputOperator, Av1Adst16Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Identity4:
DispatchRow<TSample, TOutputOperator, Identity4Operator>(
DispatchRow<TSample, TOutputOperator, Av1Identity4Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Identity8:
DispatchRow<TSample, TOutputOperator, Identity8Operator>(
DispatchRow<TSample, TOutputOperator, Av1Identity8Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Identity16:
DispatchRow<TSample, TOutputOperator, Identity16Operator>(
DispatchRow<TSample, TOutputOperator, Av1Identity16Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Identity32:
DispatchRow<TSample, TOutputOperator, Identity32Operator>(
DispatchRow<TSample, TOutputOperator, Av1Identity32Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
@ -271,67 +174,67 @@ internal static partial class Av1InverseTransformer
int bitDepth)
where TSample : unmanaged
where TOutputOperator : struct, IAv1InverseTransformOutputOperator<TSample>
where TColumnOperator : struct, IAv1InverseTransform1dOperator
where TColumnOperator : struct, IAv1Transform1dOperator
{
switch (config.TransformFunctionTypeRow)
{
case Av1TransformFunctionType.Dct4:
Transform2d<TSample, TOutputOperator, TColumnOperator, Dct4Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Dct4Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Dct8:
Transform2d<TSample, TOutputOperator, TColumnOperator, Dct8Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Dct8Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Dct16:
Transform2d<TSample, TOutputOperator, TColumnOperator, Dct16Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Dct16Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Dct32:
Transform2d<TSample, TOutputOperator, TColumnOperator, Dct32Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Dct32Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Dct64:
Transform2d<TSample, TOutputOperator, TColumnOperator, Dct64Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Dct64Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Adst4:
Transform2d<TSample, TOutputOperator, TColumnOperator, Adst4Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Adst4Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Adst8:
Transform2d<TSample, TOutputOperator, TColumnOperator, Adst8Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Adst8Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Adst16:
Transform2d<TSample, TOutputOperator, TColumnOperator, Adst16Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Adst16Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Identity4:
Transform2d<TSample, TOutputOperator, TColumnOperator, Identity4Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Identity4Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Identity8:
Transform2d<TSample, TOutputOperator, TColumnOperator, Identity8Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Identity8Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Identity16:
Transform2d<TSample, TOutputOperator, TColumnOperator, Identity16Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Identity16Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
case Av1TransformFunctionType.Identity32:
Transform2d<TSample, TOutputOperator, TColumnOperator, Identity32Operator>(
Transform2d<TSample, TOutputOperator, TColumnOperator, Av1Identity32Inverse1dOperator>(
input, outputForRead, strideForRead, outputForWrite, strideForWrite, ref config, workspace, bitDepth);
break;
@ -354,8 +257,8 @@ internal static partial class Av1InverseTransformer
int bitDepth)
where TSample : unmanaged
where TOutputOperator : struct, IAv1InverseTransformOutputOperator<TSample>
where TColumnOperator : struct, IAv1InverseTransform1dOperator
where TRowOperator : struct, IAv1InverseTransform1dOperator
where TColumnOperator : struct, IAv1Transform1dOperator
where TRowOperator : struct, IAv1Transform1dOperator
{
int width = config.TransformSize.GetWidth();
int height = config.TransformSize.GetHeight();
@ -406,8 +309,8 @@ internal static partial class Av1InverseTransformer
int bitDepth)
where TSample : unmanaged
where TOutputOperator : struct, IAv1InverseTransformOutputOperator<TSample>
where TColumnOperator : struct, IAv1InverseTransform1dOperator
where TRowOperator : struct, IAv1InverseTransform1dOperator
where TColumnOperator : struct, IAv1Transform1dOperator
where TRowOperator : struct, IAv1Transform1dOperator
{
const int laneCount = 8;
const int vectorLength = Av1Constants.MaxTransformSize * laneCount;
@ -562,8 +465,8 @@ internal static partial class Av1InverseTransformer
int bitDepth)
where TSample : unmanaged
where TOutputOperator : struct, IAv1InverseTransformOutputOperator<TSample>
where TColumnOperator : struct, IAv1InverseTransform1dOperator
where TRowOperator : struct, IAv1InverseTransform1dOperator
where TColumnOperator : struct, IAv1Transform1dOperator
where TRowOperator : struct, IAv1Transform1dOperator
{
const int laneCount = 4;
const int vectorLength = Av1Constants.MaxTransformSize * laneCount;
@ -694,8 +597,8 @@ internal static partial class Av1InverseTransformer
int bitDepth)
where TSample : unmanaged
where TOutputOperator : struct, IAv1InverseTransformOutputOperator<TSample>
where TColumnOperator : struct, IAv1InverseTransform1dOperator
where TRowOperator : struct, IAv1InverseTransform1dOperator
where TColumnOperator : struct, IAv1Transform1dOperator
where TRowOperator : struct, IAv1Transform1dOperator
{
int width = config.TransformSize.GetWidth();
int height = config.TransformSize.GetHeight();

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

@ -0,0 +1,94 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Common.Helpers;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Reconstructs AV1 samples from predicted values and inverse-transform residuals.
/// </summary>
/// <remarks>
/// Each SIMD lane represents one consecutive reconstructed sample. Packed byte or 16-bit predictions are widened to
/// signed 32-bit lanes before residual addition, clipped to the coded sample range, and narrowed into exact-width
/// stores. The closed <typeparamref name="TSample"/> specialization removes storage-type branches from hot loops.
/// </remarks>
/// <typeparam name="TSample">The decoded sample storage type.</typeparam>
internal readonly struct Av1InverseTransformOutputOperator<TSample> : IAv1InverseTransformOutputOperator<TSample>
where TSample : unmanaged
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TSample Add(TSample prediction, int residual, int bitDepth)
{
// TSample is fixed by the byte and short decoder entry points. The JIT removes this type test from each
// closed transform so storage selection does not introduce a branch in the reconstruction loop.
if (typeof(TSample) == typeof(byte))
{
byte value = (byte)Math.Clamp(Unsafe.As<TSample, byte>(ref prediction) + residual, byte.MinValue, byte.MaxValue);
return Unsafe.As<byte, TSample>(ref value);
}
short result = (short)Math.Clamp(Unsafe.As<TSample, short>(ref prediction) + residual, 0, (1 << bitDepth) - 1);
return Unsafe.As<short, TSample>(ref result);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref TSample prediction, ref TSample destination, Vector128<int> residual, int bitDepth)
{
if (typeof(TSample) == typeof(byte))
{
// Read and write exactly four bytes. The unused upper lanes only participate in narrowing and never reach
// memory, which keeps reconstruction valid at a tightly packed row boundary.
ref byte source = ref Unsafe.As<TSample, byte>(ref prediction);
uint packed = Unsafe.ReadUnaligned<uint>(ref source);
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
Vector128<int> predicted32 = Vector128.WidenLower(predicted16).AsInt32();
Vector128<int> reconstructed = Vector128.Clamp(predicted32 + residual, Vector128<int>.Zero, Vector128.Create((int)byte.MaxValue));
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.AsUInt32(), Vector128<uint>.Zero);
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<TSample, byte>(ref destination), reconstructed8.AsUInt32().ToScalar());
return;
}
ref short highBitDepthSource = ref Unsafe.As<TSample, short>(ref prediction);
ulong highBitDepthPacked = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<short, byte>(ref highBitDepthSource));
Vector128<int> highBitDepthPredicted = Vector128.WidenLower(Vector128.CreateScalarUnsafe(highBitDepthPacked).AsInt16());
Vector128<int> highBitDepthReconstructed =
Vector128.Clamp(highBitDepthPredicted + residual, Vector128<int>.Zero, Vector128.Create((1 << bitDepth) - 1));
Vector128<short> narrowed = Vector128.Narrow(highBitDepthReconstructed, Vector128<int>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<TSample, byte>(ref destination), narrowed.AsUInt64().ToScalar());
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref TSample prediction, ref TSample destination, Vector256<int> residual, int bitDepth)
{
if (typeof(TSample) == typeof(byte))
{
// Eight byte predictions widen through UInt16 into the eight Int32 residual lanes. The final 64-bit store
// covers only those reconstructed samples and does not require destination padding.
ref byte source = ref Unsafe.As<TSample, byte>(ref prediction);
ulong packed = Unsafe.ReadUnaligned<ulong>(ref source);
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
Vector256<int> predicted32 = Vector256.Create(Vector128.WidenLower(predicted16), Vector128.WidenUpper(predicted16)).AsInt32();
Vector256<int> reconstructed = Vector256.Clamp(predicted32 + residual, Vector256<int>.Zero, Vector256.Create((int)byte.MaxValue));
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.GetLower().AsUInt32(), reconstructed.GetUpper().AsUInt32());
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<TSample, byte>(ref destination), reconstructed8.AsUInt64().ToScalar());
return;
}
ref short highBitDepthSource = ref Unsafe.As<TSample, short>(ref prediction);
Vector256<int> highBitDepthPredicted = Vector256_.Widen(Vector128.LoadUnsafe(ref highBitDepthSource));
Vector256<int> highBitDepthReconstructed =
Vector256.Clamp(highBitDepthPredicted + residual, Vector256<int>.Zero, Vector256.Create((1 << bitDepth) - 1));
Vector128<short> narrowed = Vector128.Narrow(highBitDepthReconstructed.GetLower(), highBitDepthReconstructed.GetUpper());
narrowed.StoreUnsafe(ref Unsafe.As<TSample, short>(ref destination));
}
}

574
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Adst16Operator.cs

@ -1,574 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the sixteen-point asymmetric discrete sine inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the 16-point AV1 inverse asymmetric discrete sine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Adst16Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative 16-point AV1 inverse asymmetric discrete sine transform.
/// </summary>
/// <param name="input">The sixteen frequency-domain coefficients.</param>
/// <param name="output">The sixteen spatial-domain residual values.</param>
/// <param name="step">The sixteen-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output[0] = input[15];
output[1] = input[0];
output[2] = input[13];
output[3] = input[2];
output[4] = input[11];
output[5] = input[4];
output[6] = input[9];
output[7] = input[6];
output[8] = input[7];
output[9] = input[8];
output[10] = input[5];
output[11] = input[10];
output[12] = input[3];
output[13] = input[12];
output[14] = input[1];
output[15] = input[14];
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step[0] = Av1Transform1dMath.HalfButterfly(cospi[2], output[0], cospi[62], output[1], cosBit);
step[1] = Av1Transform1dMath.HalfButterfly(cospi[62], output[0], -cospi[2], output[1], cosBit);
step[2] = Av1Transform1dMath.HalfButterfly(cospi[10], output[2], cospi[54], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[54], output[2], -cospi[10], output[3], cosBit);
step[4] = Av1Transform1dMath.HalfButterfly(cospi[18], output[4], cospi[46], output[5], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[46], output[4], -cospi[18], output[5], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(cospi[26], output[6], cospi[38], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[38], output[6], -cospi[26], output[7], cosBit);
step[8] = Av1Transform1dMath.HalfButterfly(cospi[34], output[8], cospi[30], output[9], cosBit);
step[9] = Av1Transform1dMath.HalfButterfly(cospi[30], output[8], -cospi[34], output[9], cosBit);
step[10] = Av1Transform1dMath.HalfButterfly(cospi[42], output[10], cospi[22], output[11], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(cospi[22], output[10], -cospi[42], output[11], cosBit);
step[12] = Av1Transform1dMath.HalfButterfly(cospi[50], output[12], cospi[14], output[13], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[14], output[12], -cospi[50], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(cospi[58], output[14], cospi[6], output[15], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[6], output[14], -cospi[58], output[15], cosBit);
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[8], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[9], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[10], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[11], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[12], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[5] + step[13], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[6] + step[14], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[7] + step[15], stageRange[stage]);
output[8] = Av1Transform1dMath.Clamp(step[0] - step[8], stageRange[stage]);
output[9] = Av1Transform1dMath.Clamp(step[1] - step[9], stageRange[stage]);
output[10] = Av1Transform1dMath.Clamp(step[2] - step[10], stageRange[stage]);
output[11] = Av1Transform1dMath.Clamp(step[3] - step[11], stageRange[stage]);
output[12] = Av1Transform1dMath.Clamp(step[4] - step[12], stageRange[stage]);
output[13] = Av1Transform1dMath.Clamp(step[5] - step[13], stageRange[stage]);
output[14] = Av1Transform1dMath.Clamp(step[6] - step[14], stageRange[stage]);
output[15] = Av1Transform1dMath.Clamp(step[7] - step[15], stageRange[stage]);
// Stage 4 reverses the pi/16 rotations in the upper half.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = output[4];
step[5] = output[5];
step[6] = output[6];
step[7] = output[7];
step[8] = Av1Transform1dMath.HalfButterfly(cospi[8], output[8], cospi[56], output[9], cosBit);
step[9] = Av1Transform1dMath.HalfButterfly(cospi[56], output[8], -cospi[8], output[9], cosBit);
step[10] = Av1Transform1dMath.HalfButterfly(cospi[40], output[10], cospi[24], output[11], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(cospi[24], output[10], -cospi[40], output[11], cosBit);
step[12] = Av1Transform1dMath.HalfButterfly(-cospi[56], output[12], cospi[8], output[13], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[8], output[12], cospi[56], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(-cospi[24], output[14], cospi[40], output[15], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[40], output[14], cospi[24], output[15], cosBit);
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[4], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[5], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[6], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[7], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[0] - step[4], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[1] - step[5], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[2] - step[6], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[3] - step[7], stageRange[stage]);
output[8] = Av1Transform1dMath.Clamp(step[8] + step[12], stageRange[stage]);
output[9] = Av1Transform1dMath.Clamp(step[9] + step[13], stageRange[stage]);
output[10] = Av1Transform1dMath.Clamp(step[10] + step[14], stageRange[stage]);
output[11] = Av1Transform1dMath.Clamp(step[11] + step[15], stageRange[stage]);
output[12] = Av1Transform1dMath.Clamp(step[8] - step[12], stageRange[stage]);
output[13] = Av1Transform1dMath.Clamp(step[9] - step[13], stageRange[stage]);
output[14] = Av1Transform1dMath.Clamp(step[10] - step[14], stageRange[stage]);
output[15] = Av1Transform1dMath.Clamp(step[11] - step[15], stageRange[stage]);
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = Av1Transform1dMath.HalfButterfly(cospi[16], output[4], cospi[48], output[5], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[48], output[4], -cospi[16], output[5], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[6], cospi[16], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[16], output[6], cospi[48], output[7], cosBit);
step[8] = output[8];
step[9] = output[9];
step[10] = output[10];
step[11] = output[11];
step[12] = Av1Transform1dMath.HalfButterfly(cospi[16], output[12], cospi[48], output[13], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[48], output[12], -cospi[16], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[14], cospi[16], output[15], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[16], output[14], cospi[48], output[15], cosBit);
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[2], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[3], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[0] - step[2], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[1] - step[3], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[6], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[5] + step[7], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[4] - step[6], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[5] - step[7], stageRange[stage]);
output[8] = Av1Transform1dMath.Clamp(step[8] + step[10], stageRange[stage]);
output[9] = Av1Transform1dMath.Clamp(step[9] + step[11], stageRange[stage]);
output[10] = Av1Transform1dMath.Clamp(step[8] - step[10], stageRange[stage]);
output[11] = Av1Transform1dMath.Clamp(step[9] - step[11], stageRange[stage]);
output[12] = Av1Transform1dMath.Clamp(step[12] + step[14], stageRange[stage]);
output[13] = Av1Transform1dMath.Clamp(step[13] + step[15], stageRange[stage]);
output[14] = Av1Transform1dMath.Clamp(step[12] - step[14], stageRange[stage]);
output[15] = Av1Transform1dMath.Clamp(step[13] - step[15], stageRange[stage]);
// Stage 8 reverses the pi/4 rotations for the middle pairs.
step[0] = output[0];
step[1] = output[1];
step[2] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], cospi[32], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], -cospi[32], output[3], cosBit);
step[4] = output[4];
step[5] = output[5];
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], cospi[32], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], -cospi[32], output[7], cosBit);
step[8] = output[8];
step[9] = output[9];
step[10] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], cospi[32], output[11], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], -cospi[32], output[11], cosBit);
step[12] = output[12];
step[13] = output[13];
step[14] = Av1Transform1dMath.HalfButterfly(cospi[32], output[14], cospi[32], output[15], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[32], output[14], -cospi[32], output[15], cosBit);
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
output[0] = step[0];
output[1] = -step[8];
output[2] = step[12];
output[3] = -step[4];
output[4] = step[6];
output[5] = -step[14];
output[6] = step[10];
output[7] = -step[2];
output[8] = step[3];
output[9] = -step[11];
output[10] = step[15];
output[11] = -step[7];
output[12] = step[5];
output[13] = -step[13];
output[14] = step[9];
output[15] = -step[1];
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output.V0 = input.V15;
output.V1 = input.V0;
output.V2 = input.V13;
output.V3 = input.V2;
output.V4 = input.V11;
output.V5 = input.V4;
output.V6 = input.V9;
output.V7 = input.V6;
output.V8 = input.V7;
output.V9 = input.V8;
output.V10 = input.V5;
output.V11 = input.V10;
output.V12 = input.V3;
output.V13 = input.V12;
output.V14 = input.V1;
output.V15 = input.V14;
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[2], output.V0, cospi[62], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[62], output.V0, -cospi[2], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[10], output.V2, cospi[54], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[54], output.V2, -cospi[10], output.V3, cosBit);
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[18], output.V4, cospi[46], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[46], output.V4, -cospi[18], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[26], output.V6, cospi[38], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[38], output.V6, -cospi[26], output.V7, cosBit);
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[34], output.V8, cospi[30], output.V9, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[30], output.V8, -cospi[34], output.V9, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[42], output.V10, cospi[22], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[22], output.V10, -cospi[42], output.V11, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[50], output.V12, cospi[14], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[14], output.V12, -cospi[50], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[58], output.V14, cospi[6], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[6], output.V14, -cospi[58], output.V15, cosBit);
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V8, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V9, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V10, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V11, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V12, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V13, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V14, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V15, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V0 - step.V8, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V1 - step.V9, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V2 - step.V10, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V3 - step.V11, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V4 - step.V12, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V5 - step.V13, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V6 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V7 - step.V15, stageRange[stage]);
// Stage 4 reverses the pi/16 rotations in the upper half.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = output.V6;
step.V7 = output.V7;
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V8, cospi[56], output.V9, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V8, -cospi[8], output.V9, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V10, cospi[24], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V10, -cospi[40], output.V11, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(-cospi[56], output.V12, cospi[8], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V12, cospi[56], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[24], output.V14, cospi[40], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V14, cospi[24], output.V15, cosBit);
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V12, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V13, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V10 + step.V14, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V11 + step.V15, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V8 - step.V12, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V9 - step.V13, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V10 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V11 - step.V15, stageRange[stage]);
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = output.V10;
step.V11 = output.V11;
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V12, cospi[48], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V12, -cospi[16], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V14, cospi[16], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V14, cospi[48], output.V15, cosBit);
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V10, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V11, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V8 - step.V10, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V9 - step.V11, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V14, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V13 + step.V15, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V12 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V13 - step.V15, stageRange[stage]);
// Stage 8 reverses the pi/4 rotations for the middle pairs.
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit);
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, -cospi[32], output.V11, cosBit);
step.V12 = output.V12;
step.V13 = output.V13;
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, cospi[32], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, -cospi[32], output.V15, cosBit);
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
output.V0 = step.V0;
output.V1 = -step.V8;
output.V2 = step.V12;
output.V3 = -step.V4;
output.V4 = step.V6;
output.V5 = -step.V14;
output.V6 = step.V10;
output.V7 = -step.V2;
output.V8 = step.V3;
output.V9 = -step.V11;
output.V10 = step.V15;
output.V11 = -step.V7;
output.V12 = step.V5;
output.V13 = -step.V13;
output.V14 = step.V9;
output.V15 = -step.V1;
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output.V0 = input.V15;
output.V1 = input.V0;
output.V2 = input.V13;
output.V3 = input.V2;
output.V4 = input.V11;
output.V5 = input.V4;
output.V6 = input.V9;
output.V7 = input.V6;
output.V8 = input.V7;
output.V9 = input.V8;
output.V10 = input.V5;
output.V11 = input.V10;
output.V12 = input.V3;
output.V13 = input.V12;
output.V14 = input.V1;
output.V15 = input.V14;
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[2], output.V0, cospi[62], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[62], output.V0, -cospi[2], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[10], output.V2, cospi[54], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[54], output.V2, -cospi[10], output.V3, cosBit);
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[18], output.V4, cospi[46], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[46], output.V4, -cospi[18], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[26], output.V6, cospi[38], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[38], output.V6, -cospi[26], output.V7, cosBit);
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[34], output.V8, cospi[30], output.V9, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[30], output.V8, -cospi[34], output.V9, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[42], output.V10, cospi[22], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[22], output.V10, -cospi[42], output.V11, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[50], output.V12, cospi[14], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[14], output.V12, -cospi[50], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[58], output.V14, cospi[6], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[6], output.V14, -cospi[58], output.V15, cosBit);
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V8, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V9, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V10, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V11, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V12, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V13, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V14, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V15, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V0 - step.V8, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V1 - step.V9, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V2 - step.V10, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V3 - step.V11, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V4 - step.V12, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V5 - step.V13, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V6 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V7 - step.V15, stageRange[stage]);
// Stage 4 reverses the pi/16 rotations in the upper half.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = output.V6;
step.V7 = output.V7;
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V8, cospi[56], output.V9, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V8, -cospi[8], output.V9, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V10, cospi[24], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V10, -cospi[40], output.V11, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(-cospi[56], output.V12, cospi[8], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V12, cospi[56], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[24], output.V14, cospi[40], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V14, cospi[24], output.V15, cosBit);
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V12, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V13, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V10 + step.V14, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V11 + step.V15, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V8 - step.V12, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V9 - step.V13, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V10 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V11 - step.V15, stageRange[stage]);
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = output.V10;
step.V11 = output.V11;
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V12, cospi[48], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V12, -cospi[16], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V14, cospi[16], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V14, cospi[48], output.V15, cosBit);
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V10, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V11, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V8 - step.V10, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V9 - step.V11, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V14, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V13 + step.V15, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V12 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V13 - step.V15, stageRange[stage]);
// Stage 8 reverses the pi/4 rotations for the middle pairs.
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit);
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, -cospi[32], output.V11, cosBit);
step.V12 = output.V12;
step.V13 = output.V13;
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, cospi[32], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, -cospi[32], output.V15, cosBit);
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
output.V0 = step.V0;
output.V1 = -step.V8;
output.V2 = step.V12;
output.V3 = -step.V4;
output.V4 = step.V6;
output.V5 = -step.V14;
output.V6 = step.V10;
output.V7 = -step.V2;
output.V8 = step.V3;
output.V9 = -step.V11;
output.V10 = step.V15;
output.V11 = -step.V7;
output.V12 = step.V5;
output.V13 = -step.V13;
output.V14 = step.V9;
output.V15 = -step.V1;
}
}
}

174
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Adst4Operator.cs

@ -1,174 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the four-point asymmetric discrete sine inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the four-point AV1 inverse asymmetric discrete sine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Adst4Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative four-point AV1 inverse asymmetric discrete sine transform.
/// </summary>
/// <param name="input">The four frequency-domain coefficients.</param>
/// <param name="output">The four spatial-domain residual values.</param>
/// <param name="step">The stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit);
// libaom widens the complete four-point factorization because the products retain their fixed-point scale
// until the final shift. The stage buffer is therefore unnecessary for this transform size.
long x0 = input[0];
long x1 = input[1];
long x2 = input[2];
long x3 = input[3];
_ = step;
_ = stageRange;
// Avoid the multiplications for the all-zero coefficient vector, matching libaom's scalar kernel.
if ((x0 | x1 | x2 | x3) == 0)
{
output[..4].Clear();
return;
}
// Stages 1 and 2 form the seven sine products and the one unscaled combination used by stage 3.
long s0 = sinpi[1] * x0;
long s1 = sinpi[2] * x0;
long s2 = sinpi[3] * x1;
long s3 = sinpi[4] * x2;
long s4 = sinpi[1] * x2;
long s5 = sinpi[2] * x3;
long s6 = sinpi[4] * x3;
long s7 = (x0 - x2) + x3;
// Stages 3 through 6 combine the products while preserving the fixed-point scale until the final rounding.
s0 += s3;
s1 -= s4;
s3 = s2;
s2 = sinpi[3] * s7;
s0 += s5;
s1 -= s6;
x0 = s0 + s3;
x1 = s1 + s3;
x2 = s2;
x3 = (s0 + s1) - s3;
output[0] = Av1Math.RoundShift(x0, cosBit);
output[1] = Av1Math.RoundShift(x1, cosBit);
output[2] = Av1Math.RoundShift(x2, cosBit);
output[3] = Av1Math.RoundShift(x3, cosBit);
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
TransformCore(ref input, ref output, cosBit, stageRange[0] >= WidenedIntermediateBitCount);
_ = step;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
TransformCore(ref input, ref output, cosBit, stageRange[0] >= WidenedIntermediateBitCount);
_ = step;
}
/// <summary>
/// Applies the inverse four-point matrix to four independent axes.
/// </summary>
/// <param name="input">The source values for four transform axes.</param>
/// <param name="output">The destination values for four transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
/// <param name="widenedRound">Whether the terminal fixed-point rounding requires signed 64-bit lanes.</param>
private static void TransformCore(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
int cosBit,
bool widenedRound)
{
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit);
Vector128<int> x0 = input.V0;
Vector128<int> x1 = input.V1;
Vector128<int> x2 = input.V2;
Vector128<int> x3 = input.V3;
// Pinned libaom retains the sine-table scale in Int32 products and sums, but performs the twelve-bit row
// kernel's terminal scaling and rounding in Int64. This is the only stage whose rounding bias can overflow
// a valid Int32 fixed-point sum.
if (widenedRound)
{
output.V0 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit);
output.V1 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit);
output.V2 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit);
output.V3 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit);
return;
}
output.V0 = Av1Transform1dMath.MultiplyAdd4(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit);
output.V1 = Av1Transform1dMath.MultiplyAdd4(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit);
output.V2 = Av1Transform1dMath.MultiplyAdd4(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit);
output.V3 = Av1Transform1dMath.MultiplyAdd4(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit);
}
/// <summary>
/// Applies the inverse four-point matrix to eight independent axes.
/// </summary>
/// <param name="input">The source values for eight transform axes.</param>
/// <param name="output">The destination values for eight transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
/// <param name="widenedRound">Whether the terminal fixed-point rounding requires signed 64-bit lanes.</param>
private static void TransformCore(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
int cosBit,
bool widenedRound)
{
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit);
Vector256<int> x0 = input.V0;
Vector256<int> x1 = input.V1;
Vector256<int> x2 = input.V2;
Vector256<int> x3 = input.V3;
if (widenedRound)
{
output.V0 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit);
output.V1 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit);
output.V2 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit);
output.V3 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit);
return;
}
output.V0 = Av1Transform1dMath.MultiplyAdd4(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit);
output.V1 = Av1Transform1dMath.MultiplyAdd4(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit);
output.V2 = Av1Transform1dMath.MultiplyAdd4(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit);
output.V3 = Av1Transform1dMath.MultiplyAdd4(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit);
}
}
}

295
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Adst8Operator.cs

@ -1,295 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the eight-point asymmetric discrete sine inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the eight-point AV1 inverse asymmetric discrete sine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Adst8Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative eight-point AV1 inverse asymmetric discrete sine transform.
/// </summary>
/// <param name="input">The eight frequency-domain coefficients.</param>
/// <param name="output">The eight spatial-domain residual values.</param>
/// <param name="step">The eight-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output[0] = input[7];
output[1] = input[0];
output[2] = input[5];
output[3] = input[2];
output[4] = input[3];
output[5] = input[4];
output[6] = input[1];
output[7] = input[6];
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step[0] = Av1Transform1dMath.HalfButterfly(cospi[4], output[0], cospi[60], output[1], cosBit);
step[1] = Av1Transform1dMath.HalfButterfly(cospi[60], output[0], -cospi[4], output[1], cosBit);
step[2] = Av1Transform1dMath.HalfButterfly(cospi[20], output[2], cospi[44], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[44], output[2], -cospi[20], output[3], cosBit);
step[4] = Av1Transform1dMath.HalfButterfly(cospi[36], output[4], cospi[28], output[5], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[28], output[4], -cospi[36], output[5], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(cospi[52], output[6], cospi[12], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[12], output[6], -cospi[52], output[7], cosBit);
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[4], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[5], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[6], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[7], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[0] - step[4], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[1] - step[5], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[2] - step[6], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[3] - step[7], stageRange[stage]);
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = Av1Transform1dMath.HalfButterfly(cospi[16], output[4], cospi[48], output[5], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[48], output[4], -cospi[16], output[5], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[6], cospi[16], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[16], output[6], cospi[48], output[7], cosBit);
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[2], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[3], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[0] - step[2], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[1] - step[3], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[6], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[5] + step[7], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[4] - step[6], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[5] - step[7], stageRange[stage]);
// Stage 6 reverses the pi/4 rotations for the middle pairs.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], cospi[32], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], -cospi[32], output[3], cosBit);
step[4] = output[4];
step[5] = output[5];
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], cospi[32], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], -cospi[32], output[7], cosBit);
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
output[0] = step[0];
output[1] = -step[4];
output[2] = step[6];
output[3] = -step[2];
output[4] = step[3];
output[5] = -step[7];
output[6] = step[5];
output[7] = -step[1];
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output.V0 = input.V7;
output.V1 = input.V0;
output.V2 = input.V5;
output.V3 = input.V2;
output.V4 = input.V3;
output.V5 = input.V4;
output.V6 = input.V1;
output.V7 = input.V6;
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V0, cospi[60], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V0, -cospi[4], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V2, cospi[44], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V2, -cospi[20], output.V3, cosBit);
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V4, cospi[28], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V4, -cospi[36], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V6, cospi[12], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V6, -cospi[52], output.V7, cosBit);
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]);
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit);
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]);
// Stage 6 reverses the pi/4 rotations for the middle pairs.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit);
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit);
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
output.V0 = step.V0;
output.V1 = -step.V4;
output.V2 = step.V6;
output.V3 = -step.V2;
output.V4 = step.V3;
output.V5 = -step.V7;
output.V6 = step.V5;
output.V7 = -step.V1;
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output.V0 = input.V7;
output.V1 = input.V0;
output.V2 = input.V5;
output.V3 = input.V2;
output.V4 = input.V3;
output.V5 = input.V4;
output.V6 = input.V1;
output.V7 = input.V6;
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V0, cospi[60], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V0, -cospi[4], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V2, cospi[44], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V2, -cospi[20], output.V3, cosBit);
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V4, cospi[28], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V4, -cospi[36], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V6, cospi[12], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V6, -cospi[52], output.V7, cosBit);
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]);
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit);
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]);
// Stage 6 reverses the pi/4 rotations for the middle pairs.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit);
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit);
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
output.V0 = step.V0;
output.V1 = -step.V4;
output.V2 = step.V6;
output.V3 = -step.V2;
output.V4 = step.V3;
output.V5 = -step.V7;
output.V6 = step.V5;
output.V7 = -step.V1;
}
}
}

481
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct16Operator.cs

@ -1,481 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the sixteen-point discrete cosine inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the 16-point AV1 inverse discrete cosine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Dct16Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative 16-point AV1 inverse discrete cosine transform.
/// </summary>
/// <param name="input">The sixteen frequency-domain coefficients.</param>
/// <param name="output">The sixteen spatial-domain residual values.</param>
/// <param name="step">The sixteen-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output[0] = input[0];
output[1] = input[8];
output[2] = input[4];
output[3] = input[12];
output[4] = input[2];
output[5] = input[10];
output[6] = input[6];
output[7] = input[14];
output[8] = input[1];
output[9] = input[9];
output[10] = input[5];
output[11] = input[13];
output[12] = input[3];
output[13] = input[11];
output[14] = input[7];
output[15] = input[15];
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = output[4];
step[5] = output[5];
step[6] = output[6];
step[7] = output[7];
step[8] = Av1Transform1dMath.HalfButterfly(cospi[60], output[8], -cospi[4], output[15], cosBit);
step[9] = Av1Transform1dMath.HalfButterfly(cospi[28], output[9], -cospi[36], output[14], cosBit);
step[10] = Av1Transform1dMath.HalfButterfly(cospi[44], output[10], -cospi[20], output[13], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(cospi[12], output[11], -cospi[52], output[12], cosBit);
step[12] = Av1Transform1dMath.HalfButterfly(cospi[52], output[11], cospi[12], output[12], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[20], output[10], cospi[44], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(cospi[36], output[9], cospi[28], output[14], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[4], output[8], cospi[60], output[15], cosBit);
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output[0] = step[0];
output[1] = step[1];
output[2] = step[2];
output[3] = step[3];
output[4] = Av1Transform1dMath.HalfButterfly(cospi[56], step[4], -cospi[8], step[7], cosBit);
output[5] = Av1Transform1dMath.HalfButterfly(cospi[24], step[5], -cospi[40], step[6], cosBit);
output[6] = Av1Transform1dMath.HalfButterfly(cospi[40], step[5], cospi[24], step[6], cosBit);
output[7] = Av1Transform1dMath.HalfButterfly(cospi[8], step[4], cospi[56], step[7], cosBit);
output[8] = Av1Transform1dMath.Clamp(step[8] + step[9], range);
output[9] = Av1Transform1dMath.Clamp(step[8] - step[9], range);
output[10] = Av1Transform1dMath.Clamp(step[11] - step[10], range);
output[11] = Av1Transform1dMath.Clamp(step[10] + step[11], range);
output[12] = Av1Transform1dMath.Clamp(step[12] + step[13], range);
output[13] = Av1Transform1dMath.Clamp(step[12] - step[13], range);
output[14] = Av1Transform1dMath.Clamp(step[15] - step[14], range);
output[15] = Av1Transform1dMath.Clamp(step[14] + step[15], range);
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
stage++;
range = stageRange[stage];
step[0] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], cospi[32], output[1], cosBit);
step[1] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], -cospi[32], output[1], cosBit);
step[2] = Av1Transform1dMath.HalfButterfly(cospi[48], output[2], -cospi[16], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[16], output[2], cospi[48], output[3], cosBit);
step[4] = Av1Transform1dMath.Clamp(output[4] + output[5], range);
step[5] = Av1Transform1dMath.Clamp(output[4] - output[5], range);
step[6] = Av1Transform1dMath.Clamp(output[7] - output[6], range);
step[7] = Av1Transform1dMath.Clamp(output[6] + output[7], range);
step[8] = output[8];
step[9] = Av1Transform1dMath.HalfButterfly(-cospi[16], output[9], cospi[48], output[14], cosBit);
step[10] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[10], -cospi[16], output[13], cosBit);
step[11] = output[11];
step[12] = output[12];
step[13] = Av1Transform1dMath.HalfButterfly(-cospi[16], output[10], cospi[48], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(cospi[48], output[9], cospi[16], output[14], cosBit);
step[15] = output[15];
// Stage 5 widens the reconstructed groups through their next butterfly level.
stage++;
range = stageRange[stage];
output[0] = Av1Transform1dMath.Clamp(step[0] + step[3], range);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[2], range);
output[2] = Av1Transform1dMath.Clamp(step[1] - step[2], range);
output[3] = Av1Transform1dMath.Clamp(step[0] - step[3], range);
output[4] = step[4];
output[5] = Av1Transform1dMath.HalfButterfly(-cospi[32], step[5], cospi[32], step[6], cosBit);
output[6] = Av1Transform1dMath.HalfButterfly(cospi[32], step[5], cospi[32], step[6], cosBit);
output[7] = step[7];
output[8] = Av1Transform1dMath.Clamp(step[8] + step[11], range);
output[9] = Av1Transform1dMath.Clamp(step[9] + step[10], range);
output[10] = Av1Transform1dMath.Clamp(step[9] - step[10], range);
output[11] = Av1Transform1dMath.Clamp(step[8] - step[11], range);
output[12] = Av1Transform1dMath.Clamp(step[15] - step[12], range);
output[13] = Av1Transform1dMath.Clamp(step[14] - step[13], range);
output[14] = Av1Transform1dMath.Clamp(step[13] + step[14], range);
output[15] = Av1Transform1dMath.Clamp(step[12] + step[15], range);
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
stage++;
range = stageRange[stage];
step[0] = Av1Transform1dMath.Clamp(output[0] + output[7], range);
step[1] = Av1Transform1dMath.Clamp(output[1] + output[6], range);
step[2] = Av1Transform1dMath.Clamp(output[2] + output[5], range);
step[3] = Av1Transform1dMath.Clamp(output[3] + output[4], range);
step[4] = Av1Transform1dMath.Clamp(output[3] - output[4], range);
step[5] = Av1Transform1dMath.Clamp(output[2] - output[5], range);
step[6] = Av1Transform1dMath.Clamp(output[1] - output[6], range);
step[7] = Av1Transform1dMath.Clamp(output[0] - output[7], range);
step[8] = output[8];
step[9] = output[9];
step[10] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[10], cospi[32], output[13], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[11], cospi[32], output[12], cosBit);
step[12] = Av1Transform1dMath.HalfButterfly(cospi[32], output[11], cospi[32], output[12], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], cospi[32], output[13], cosBit);
step[14] = output[14];
step[15] = output[15];
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output[0] = Av1Transform1dMath.Clamp(step[0] + step[15], range);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[14], range);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[13], range);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[12], range);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[11], range);
output[5] = Av1Transform1dMath.Clamp(step[5] + step[10], range);
output[6] = Av1Transform1dMath.Clamp(step[6] + step[9], range);
output[7] = Av1Transform1dMath.Clamp(step[7] + step[8], range);
output[8] = Av1Transform1dMath.Clamp(step[7] - step[8], range);
output[9] = Av1Transform1dMath.Clamp(step[6] - step[9], range);
output[10] = Av1Transform1dMath.Clamp(step[5] - step[10], range);
output[11] = Av1Transform1dMath.Clamp(step[4] - step[11], range);
output[12] = Av1Transform1dMath.Clamp(step[3] - step[12], range);
output[13] = Av1Transform1dMath.Clamp(step[2] - step[13], range);
output[14] = Av1Transform1dMath.Clamp(step[1] - step[14], range);
output[15] = Av1Transform1dMath.Clamp(step[0] - step[15], range);
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output.V0 = input.V0;
output.V1 = input.V8;
output.V2 = input.V4;
output.V3 = input.V12;
output.V4 = input.V2;
output.V5 = input.V10;
output.V6 = input.V6;
output.V7 = input.V14;
output.V8 = input.V1;
output.V9 = input.V9;
output.V10 = input.V5;
output.V11 = input.V13;
output.V12 = input.V3;
output.V13 = input.V11;
output.V14 = input.V7;
output.V15 = input.V15;
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = output.V6;
step.V7 = output.V7;
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V8, -cospi[4], output.V15, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V9, -cospi[36], output.V14, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V10, -cospi[20], output.V13, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V11, -cospi[52], output.V12, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V11, cospi[12], output.V12, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V10, cospi[44], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V9, cospi[28], output.V14, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V8, cospi[60], output.V15, cosBit);
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output.V0 = step.V0;
output.V1 = step.V1;
output.V2 = step.V2;
output.V3 = step.V3;
output.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], step.V4, -cospi[8], step.V7, cosBit);
output.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], step.V5, -cospi[40], step.V6, cosBit);
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], step.V5, cospi[24], step.V6, cosBit);
output.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], step.V4, cospi[56], step.V7, cosBit);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V9, range);
output.V9 = Av1Transform1dMath.Clamp(step.V8 - step.V9, range);
output.V10 = Av1Transform1dMath.Clamp(step.V11 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V10 + step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V13, range);
output.V13 = Av1Transform1dMath.Clamp(step.V12 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V15 - step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V14 + step.V15, range);
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
stage++;
range = stageRange[stage];
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit);
step.V4 = Av1Transform1dMath.Clamp(output.V4 + output.V5, range);
step.V5 = Av1Transform1dMath.Clamp(output.V4 - output.V5, range);
step.V6 = Av1Transform1dMath.Clamp(output.V7 - output.V6, range);
step.V7 = Av1Transform1dMath.Clamp(output.V6 + output.V7, range);
step.V8 = output.V8;
step.V9 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V9, cospi[48], output.V14, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V10, -cospi[16], output.V13, cosBit);
step.V11 = output.V11;
step.V12 = output.V12;
step.V13 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V10, cospi[48], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V9, cospi[16], output.V14, cosBit);
step.V15 = output.V15;
// Stage 5 widens the reconstructed groups through their next butterfly level.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range);
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range);
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range);
output.V4 = step.V4;
output.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], step.V5, cospi[32], step.V6, cosBit);
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V5, cospi[32], step.V6, cosBit);
output.V7 = step.V7;
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V11, range);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V10, range);
output.V10 = Av1Transform1dMath.Clamp(step.V9 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V8 - step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V15 - step.V12, range);
output.V13 = Av1Transform1dMath.Clamp(step.V14 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V13 + step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V12 + step.V15, range);
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
stage++;
range = stageRange[stage];
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V7, range);
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V6, range);
step.V2 = Av1Transform1dMath.Clamp(output.V2 + output.V5, range);
step.V3 = Av1Transform1dMath.Clamp(output.V3 + output.V4, range);
step.V4 = Av1Transform1dMath.Clamp(output.V3 - output.V4, range);
step.V5 = Av1Transform1dMath.Clamp(output.V2 - output.V5, range);
step.V6 = Av1Transform1dMath.Clamp(output.V1 - output.V6, range);
step.V7 = Av1Transform1dMath.Clamp(output.V0 - output.V7, range);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V10, cospi[32], output.V13, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V11, cospi[32], output.V12, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V11, cospi[32], output.V12, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V13, cosBit);
step.V14 = output.V14;
step.V15 = output.V15;
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V15, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V14, range);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V13, range);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V12, range);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V11, range);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V10, range);
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V9, range);
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V8, range);
output.V8 = Av1Transform1dMath.Clamp(step.V7 - step.V8, range);
output.V9 = Av1Transform1dMath.Clamp(step.V6 - step.V9, range);
output.V10 = Av1Transform1dMath.Clamp(step.V5 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V4 - step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V3 - step.V12, range);
output.V13 = Av1Transform1dMath.Clamp(step.V2 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V1 - step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V0 - step.V15, range);
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output.V0 = input.V0;
output.V1 = input.V8;
output.V2 = input.V4;
output.V3 = input.V12;
output.V4 = input.V2;
output.V5 = input.V10;
output.V6 = input.V6;
output.V7 = input.V14;
output.V8 = input.V1;
output.V9 = input.V9;
output.V10 = input.V5;
output.V11 = input.V13;
output.V12 = input.V3;
output.V13 = input.V11;
output.V14 = input.V7;
output.V15 = input.V15;
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = output.V6;
step.V7 = output.V7;
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V8, -cospi[4], output.V15, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V9, -cospi[36], output.V14, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V10, -cospi[20], output.V13, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V11, -cospi[52], output.V12, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V11, cospi[12], output.V12, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V10, cospi[44], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V9, cospi[28], output.V14, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V8, cospi[60], output.V15, cosBit);
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output.V0 = step.V0;
output.V1 = step.V1;
output.V2 = step.V2;
output.V3 = step.V3;
output.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], step.V4, -cospi[8], step.V7, cosBit);
output.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], step.V5, -cospi[40], step.V6, cosBit);
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], step.V5, cospi[24], step.V6, cosBit);
output.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], step.V4, cospi[56], step.V7, cosBit);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V9, range);
output.V9 = Av1Transform1dMath.Clamp(step.V8 - step.V9, range);
output.V10 = Av1Transform1dMath.Clamp(step.V11 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V10 + step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V13, range);
output.V13 = Av1Transform1dMath.Clamp(step.V12 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V15 - step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V14 + step.V15, range);
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
stage++;
range = stageRange[stage];
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit);
step.V4 = Av1Transform1dMath.Clamp(output.V4 + output.V5, range);
step.V5 = Av1Transform1dMath.Clamp(output.V4 - output.V5, range);
step.V6 = Av1Transform1dMath.Clamp(output.V7 - output.V6, range);
step.V7 = Av1Transform1dMath.Clamp(output.V6 + output.V7, range);
step.V8 = output.V8;
step.V9 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V9, cospi[48], output.V14, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V10, -cospi[16], output.V13, cosBit);
step.V11 = output.V11;
step.V12 = output.V12;
step.V13 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V10, cospi[48], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V9, cospi[16], output.V14, cosBit);
step.V15 = output.V15;
// Stage 5 widens the reconstructed groups through their next butterfly level.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range);
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range);
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range);
output.V4 = step.V4;
output.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], step.V5, cospi[32], step.V6, cosBit);
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V5, cospi[32], step.V6, cosBit);
output.V7 = step.V7;
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V11, range);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V10, range);
output.V10 = Av1Transform1dMath.Clamp(step.V9 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V8 - step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V15 - step.V12, range);
output.V13 = Av1Transform1dMath.Clamp(step.V14 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V13 + step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V12 + step.V15, range);
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
stage++;
range = stageRange[stage];
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V7, range);
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V6, range);
step.V2 = Av1Transform1dMath.Clamp(output.V2 + output.V5, range);
step.V3 = Av1Transform1dMath.Clamp(output.V3 + output.V4, range);
step.V4 = Av1Transform1dMath.Clamp(output.V3 - output.V4, range);
step.V5 = Av1Transform1dMath.Clamp(output.V2 - output.V5, range);
step.V6 = Av1Transform1dMath.Clamp(output.V1 - output.V6, range);
step.V7 = Av1Transform1dMath.Clamp(output.V0 - output.V7, range);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V10, cospi[32], output.V13, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V11, cospi[32], output.V12, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V11, cospi[32], output.V12, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V13, cosBit);
step.V14 = output.V14;
step.V15 = output.V15;
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V15, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V14, range);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V13, range);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V12, range);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V11, range);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V10, range);
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V9, range);
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V8, range);
output.V8 = Av1Transform1dMath.Clamp(step.V7 - step.V8, range);
output.V9 = Av1Transform1dMath.Clamp(step.V6 - step.V9, range);
output.V10 = Av1Transform1dMath.Clamp(step.V5 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V4 - step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V3 - step.V12, range);
output.V13 = Av1Transform1dMath.Clamp(step.V2 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V1 - step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V0 - step.V15, range);
}
}
}

1033
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct32Operator.cs

File diff suppressed because it is too large

118
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct4Operator.cs

@ -1,118 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the four-point discrete cosine inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the four-point AV1 inverse discrete cosine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Dct4Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative four-point AV1 inverse discrete cosine transform.
/// </summary>
/// <param name="input">The four frequency-domain coefficients.</param>
/// <param name="output">The four spatial-domain residual values.</param>
/// <param name="step">The four-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
output[0] = input[0];
output[1] = input[2];
output[2] = input[1];
output[3] = input[3];
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
step[0] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], cospi[32], output[1], cosBit);
step[1] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], -cospi[32], output[1], cosBit);
step[2] = Av1Transform1dMath.HalfButterfly(cospi[48], output[2], -cospi[16], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[16], output[2], cospi[48], output[3], cosBit);
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
byte range = stageRange[3];
output[0] = Av1Transform1dMath.Clamp(step[0] + step[3], range);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[2], range);
output[2] = Av1Transform1dMath.Clamp(step[1] - step[2], range);
output[3] = Av1Transform1dMath.Clamp(step[0] - step[3], range);
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
output.V0 = input.V0;
output.V1 = input.V2;
output.V2 = input.V1;
output.V3 = input.V3;
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit);
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
byte range = stageRange[3];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range);
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range);
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range);
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
output.V0 = input.V0;
output.V1 = input.V2;
output.V2 = input.V1;
output.V3 = input.V3;
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit);
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
byte range = stageRange[3];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range);
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range);
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range);
}
}
}

2278
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct64Operator.cs

File diff suppressed because it is too large

238
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Dct8Operator.cs

@ -1,238 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the eight-point discrete cosine inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the eight-point AV1 inverse discrete cosine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Dct8Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative eight-point AV1 inverse discrete cosine transform.
/// </summary>
/// <param name="input">The eight frequency-domain coefficients.</param>
/// <param name="output">The eight spatial-domain residual values.</param>
/// <param name="step">The eight-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output[0] = input[0];
output[1] = input[4];
output[2] = input[2];
output[3] = input[6];
output[4] = input[1];
output[5] = input[5];
output[6] = input[3];
output[7] = input[7];
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = Av1Transform1dMath.HalfButterfly(cospi[56], output[4], -cospi[8], output[7], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[24], output[5], -cospi[40], output[6], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(cospi[40], output[5], cospi[24], output[6], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[8], output[4], cospi[56], output[7], cosBit);
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output[0] = Av1Transform1dMath.HalfButterfly(cospi[32], step[0], cospi[32], step[1], cosBit);
output[1] = Av1Transform1dMath.HalfButterfly(cospi[32], step[0], -cospi[32], step[1], cosBit);
output[2] = Av1Transform1dMath.HalfButterfly(cospi[48], step[2], -cospi[16], step[3], cosBit);
output[3] = Av1Transform1dMath.HalfButterfly(cospi[16], step[2], cospi[48], step[3], cosBit);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[5], range);
output[5] = Av1Transform1dMath.Clamp(step[4] - step[5], range);
output[6] = Av1Transform1dMath.Clamp(step[7] - step[6], range);
output[7] = Av1Transform1dMath.Clamp(step[6] + step[7], range);
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
stage++;
step[0] = Av1Transform1dMath.Clamp(output[0] + output[3], range);
step[1] = Av1Transform1dMath.Clamp(output[1] + output[2], range);
step[2] = Av1Transform1dMath.Clamp(output[1] - output[2], range);
step[3] = Av1Transform1dMath.Clamp(output[0] - output[3], range);
step[4] = output[4];
step[5] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[5], cospi[32], output[6], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[5], cospi[32], output[6], cosBit);
step[7] = output[7];
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output[0] = Av1Transform1dMath.Clamp(step[0] + step[7], range);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[6], range);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[5], range);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[4], range);
output[4] = Av1Transform1dMath.Clamp(step[3] - step[4], range);
output[5] = Av1Transform1dMath.Clamp(step[2] - step[5], range);
output[6] = Av1Transform1dMath.Clamp(step[1] - step[6], range);
output[7] = Av1Transform1dMath.Clamp(step[0] - step[7], range);
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output.V0 = input.V0;
output.V1 = input.V4;
output.V2 = input.V2;
output.V3 = input.V6;
output.V4 = input.V1;
output.V5 = input.V5;
output.V6 = input.V3;
output.V7 = input.V7;
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V4, -cospi[8], output.V7, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V5, -cospi[40], output.V6, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V5, cospi[24], output.V6, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V4, cospi[56], output.V7, cosBit);
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, cospi[32], step.V1, cosBit);
output.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, -cospi[32], step.V1, cosBit);
output.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], step.V2, -cospi[16], step.V3, cosBit);
output.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], step.V2, cospi[48], step.V3, cosBit);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V5, range);
output.V5 = Av1Transform1dMath.Clamp(step.V4 - step.V5, range);
output.V6 = Av1Transform1dMath.Clamp(step.V7 - step.V6, range);
output.V7 = Av1Transform1dMath.Clamp(step.V6 + step.V7, range);
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
stage++;
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V3, range);
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V2, range);
step.V2 = Av1Transform1dMath.Clamp(output.V1 - output.V2, range);
step.V3 = Av1Transform1dMath.Clamp(output.V0 - output.V3, range);
step.V4 = output.V4;
step.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V5, cospi[32], output.V6, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V5, cospi[32], output.V6, cosBit);
step.V7 = output.V7;
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V7, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V6, range);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V5, range);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V4, range);
output.V4 = Av1Transform1dMath.Clamp(step.V3 - step.V4, range);
output.V5 = Av1Transform1dMath.Clamp(step.V2 - step.V5, range);
output.V6 = Av1Transform1dMath.Clamp(step.V1 - step.V6, range);
output.V7 = Av1Transform1dMath.Clamp(step.V0 - step.V7, range);
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output.V0 = input.V0;
output.V1 = input.V4;
output.V2 = input.V2;
output.V3 = input.V6;
output.V4 = input.V1;
output.V5 = input.V5;
output.V6 = input.V3;
output.V7 = input.V7;
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V4, -cospi[8], output.V7, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V5, -cospi[40], output.V6, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V5, cospi[24], output.V6, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V4, cospi[56], output.V7, cosBit);
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, cospi[32], step.V1, cosBit);
output.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, -cospi[32], step.V1, cosBit);
output.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], step.V2, -cospi[16], step.V3, cosBit);
output.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], step.V2, cospi[48], step.V3, cosBit);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V5, range);
output.V5 = Av1Transform1dMath.Clamp(step.V4 - step.V5, range);
output.V6 = Av1Transform1dMath.Clamp(step.V7 - step.V6, range);
output.V7 = Av1Transform1dMath.Clamp(step.V6 + step.V7, range);
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
stage++;
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V3, range);
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V2, range);
step.V2 = Av1Transform1dMath.Clamp(output.V1 - output.V2, range);
step.V3 = Av1Transform1dMath.Clamp(output.V0 - output.V3, range);
step.V4 = output.V4;
step.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V5, cospi[32], output.V6, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V5, cospi[32], output.V6, cosBit);
step.V7 = output.V7;
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V7, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V6, range);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V5, range);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V4, range);
output.V4 = Av1Transform1dMath.Clamp(step.V3 - step.V4, range);
output.V5 = Av1Transform1dMath.Clamp(step.V2 - step.V5, range);
output.V6 = Av1Transform1dMath.Clamp(step.V1 - step.V6, range);
output.V7 = Av1Transform1dMath.Clamp(step.V0 - step.V7, range);
}
}
}

87
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Identity16Operator.cs

@ -1,87 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the sixteen-point identity inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the sixteen-point AV1 inverse identity transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
/// </remarks>
internal readonly struct Identity16Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative sixteen-point AV1 inverse identity transform.
/// </summary>
/// <param name="input">The sixteen frequency-domain coefficients.</param>
/// <param name="output">The sixteen scaled spatial-domain values.</param>
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
_ = step;
_ = cosBit;
_ = stageRange;
// The AV1 identity transform preserves coefficient order while applying the twice the square-root-of-two fixed-point scale required for 2-D normalization.
for (int i = 0; i < 16; i++)
{
output[i] = Av1Math.RoundShift((long)input[i] * (2 * Av1Transform1dMath.NewSqrt2), Av1Transform1dMath.NewSqrt2Bits);
}
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
// The doubled scale exceeds Int32 only for the 20-bit twelve-bit row range. Widen that exact product and
// rounding sequence, matching libaom without changing the established lower-range SIMD path.
if (stageRange[0] >= WidenedIntermediateBitCount)
{
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
else
{
Av1IdentityTransform1d.Transform(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
_ = step;
_ = cosBit;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
if (stageRange[0] >= WidenedIntermediateBitCount)
{
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
else
{
Av1IdentityTransform1d.Transform(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
_ = step;
_ = cosBit;
}
}
}

71
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Identity32Operator.cs

@ -1,71 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the thirty-two-point identity inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the thirty-two-point AV1 inverse identity transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
/// </remarks>
internal readonly struct Identity32Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative thirty-two-point AV1 inverse identity transform.
/// </summary>
/// <param name="input">The thirty-two frequency-domain coefficients.</param>
/// <param name="output">The thirty-two scaled spatial-domain values.</param>
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
_ = step;
_ = cosBit;
_ = stageRange;
// The AV1 identity transform preserves coefficient order while applying the exact factor-of-four scale required for 2-D normalization.
for (int i = 0; i < 32; i++)
{
output[i] = input[i] * 4;
}
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0);
_ = step;
_ = cosBit;
_ = stageRange;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0);
_ = step;
_ = cosBit;
_ = stageRange;
}
}
}

87
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Identity4Operator.cs

@ -1,87 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the four-point identity inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the four-point AV1 inverse identity transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
/// </remarks>
internal readonly struct Identity4Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative four-point AV1 inverse identity transform.
/// </summary>
/// <param name="input">The four frequency-domain coefficients.</param>
/// <param name="output">The four scaled spatial-domain values.</param>
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
_ = step;
_ = cosBit;
_ = stageRange;
// The AV1 identity transform preserves coefficient order while applying the square-root-of-two fixed-point scale required for 2-D normalization.
for (int i = 0; i < 4; i++)
{
output[i] = Av1Math.RoundShift((long)input[i] * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
// Only a twelve-bit row transform has the 20-bit input range that can overflow this fixed-point product.
// Match libaom's high-bit-depth kernel there while retaining the compact Int32 path for narrower ranges.
if (stageRange[0] >= WidenedIntermediateBitCount)
{
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
else
{
Av1IdentityTransform1d.Transform(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
_ = step;
_ = cosBit;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
if (stageRange[0] >= WidenedIntermediateBitCount)
{
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
else
{
Av1IdentityTransform1d.Transform(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
_ = step;
_ = cosBit;
}
}
}

71
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.Identity8Operator.cs

@ -1,71 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the eight-point identity inverse transform operator.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Defines the eight-point AV1 inverse identity transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
/// </remarks>
internal readonly struct Identity8Operator : IAv1InverseTransform1dOperator
{
/// <summary>
/// Applies the normative eight-point AV1 inverse identity transform.
/// </summary>
/// <param name="input">The eight frequency-domain coefficients.</param>
/// <param name="output">The eight scaled spatial-domain values.</param>
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
_ = step;
_ = cosBit;
_ = stageRange;
// The AV1 identity transform preserves coefficient order while applying the exact factor-of-two scale required for 2-D normalization.
for (int i = 0; i < 8; i++)
{
output[i] = input[i] * 2;
}
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0);
_ = step;
_ = cosBit;
_ = stageRange;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0);
_ = step;
_ = cosBit;
_ = stageRange;
}
}
}

100
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.OutputOperator.cs

@ -1,100 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Common.Helpers;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <content>
/// Provides the sample-output operator shared by inverse transform traversals.
/// </content>
internal static partial class Av1InverseTransformer
{
/// <summary>
/// Reconstructs AV1 samples from predicted values and inverse-transform residuals.
/// </summary>
/// <remarks>
/// Each SIMD lane represents one consecutive reconstructed sample. Packed byte or 16-bit predictions are widened to
/// signed 32-bit lanes before residual addition, clipped to the coded sample range, and narrowed into exact-width
/// stores. The closed <typeparamref name="TSample"/> specialization removes storage-type branches from hot loops.
/// </remarks>
/// <typeparam name="TSample">The decoded sample storage type.</typeparam>
internal readonly struct OutputOperator<TSample> : IAv1InverseTransformOutputOperator<TSample>
where TSample : unmanaged
{
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TSample Add(TSample prediction, int residual, int bitDepth)
{
// TSample is fixed by the byte and short decoder entry points. The JIT removes this type test from each
// closed transform so storage selection does not introduce a branch in the reconstruction loop.
if (typeof(TSample) == typeof(byte))
{
byte value = (byte)Math.Clamp(Unsafe.As<TSample, byte>(ref prediction) + residual, byte.MinValue, byte.MaxValue);
return Unsafe.As<byte, TSample>(ref value);
}
short result = (short)Math.Clamp(Unsafe.As<TSample, short>(ref prediction) + residual, 0, (1 << bitDepth) - 1);
return Unsafe.As<short, TSample>(ref result);
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref TSample prediction, ref TSample destination, Vector128<int> residual, int bitDepth)
{
if (typeof(TSample) == typeof(byte))
{
// Read and write exactly four bytes. The unused upper lanes only participate in narrowing and never reach
// memory, which keeps reconstruction valid at a tightly packed row boundary.
ref byte source = ref Unsafe.As<TSample, byte>(ref prediction);
uint packed = Unsafe.ReadUnaligned<uint>(ref source);
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
Vector128<int> predicted32 = Vector128.WidenLower(predicted16).AsInt32();
Vector128<int> reconstructed = Vector128.Clamp(predicted32 + residual, Vector128<int>.Zero, Vector128.Create((int)byte.MaxValue));
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.AsUInt32(), Vector128<uint>.Zero);
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<TSample, byte>(ref destination), reconstructed8.AsUInt32().ToScalar());
return;
}
ref short highBitDepthSource = ref Unsafe.As<TSample, short>(ref prediction);
ulong highBitDepthPacked = Unsafe.ReadUnaligned<ulong>(ref Unsafe.As<short, byte>(ref highBitDepthSource));
Vector128<int> highBitDepthPredicted = Vector128.WidenLower(Vector128.CreateScalarUnsafe(highBitDepthPacked).AsInt16());
Vector128<int> highBitDepthReconstructed =
Vector128.Clamp(highBitDepthPredicted + residual, Vector128<int>.Zero, Vector128.Create((1 << bitDepth) - 1));
Vector128<short> narrowed = Vector128.Narrow(highBitDepthReconstructed, Vector128<int>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<TSample, byte>(ref destination), narrowed.AsUInt64().ToScalar());
}
/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Add(ref TSample prediction, ref TSample destination, Vector256<int> residual, int bitDepth)
{
if (typeof(TSample) == typeof(byte))
{
// Eight byte predictions widen through UInt16 into the eight Int32 residual lanes. The final 64-bit store
// covers only those reconstructed samples and does not require destination padding.
ref byte source = ref Unsafe.As<TSample, byte>(ref prediction);
ulong packed = Unsafe.ReadUnaligned<ulong>(ref source);
Vector128<ushort> predicted16 = Vector128.WidenLower(Vector128.CreateScalarUnsafe(packed).AsByte());
Vector256<int> predicted32 = Vector256.Create(Vector128.WidenLower(predicted16), Vector128.WidenUpper(predicted16)).AsInt32();
Vector256<int> reconstructed = Vector256.Clamp(predicted32 + residual, Vector256<int>.Zero, Vector256.Create((int)byte.MaxValue));
Vector128<ushort> reconstructed16 = Vector128.Narrow(reconstructed.GetLower().AsUInt32(), reconstructed.GetUpper().AsUInt32());
Vector128<byte> reconstructed8 = Vector128.Narrow(reconstructed16, Vector128<ushort>.Zero);
Unsafe.WriteUnaligned(ref Unsafe.As<TSample, byte>(ref destination), reconstructed8.AsUInt64().ToScalar());
return;
}
ref short highBitDepthSource = ref Unsafe.As<TSample, short>(ref prediction);
Vector256<int> highBitDepthPredicted = Vector256_.Widen(Vector128.LoadUnsafe(ref highBitDepthSource));
Vector256<int> highBitDepthReconstructed =
Vector256.Clamp(highBitDepthPredicted + residual, Vector256<int>.Zero, Vector256.Create((1 << bitDepth) - 1));
Vector128<short> narrowed = Vector128.Narrow(highBitDepthReconstructed.GetLower(), highBitDepthReconstructed.GetUpper());
narrowed.StoreUnsafe(ref Unsafe.As<TSample, short>(ref destination));
}
}
}

2
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.cs

@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Reconstructs decoded AV1 transform coefficients into prediction sample buffers.
/// </summary>
internal static partial class Av1InverseTransformer
internal class Av1InverseTransformer
{
/// <summary>
/// Reconstructs an eight-bit transform block in place by adding its inverse-transform residual.

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

@ -49,7 +49,7 @@ internal static class Av1InverseTransformerFactory
transformFunctionParameters.TransformSize,
transformFunctionParameters.BitDepth);
Av1InverseTransformer.Transform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, ref config, workspace);
Av1Inverse2dTransformer.Transform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, ref config, workspace);
}
/// <summary>
@ -93,6 +93,6 @@ internal static class Av1InverseTransformerFactory
transformFunctionParameters.TransformSize,
transformFunctionParameters.BitDepth);
Av1InverseTransformer.Transform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, ref config, workspace, transformFunctionParameters.BitDepth);
Av1Inverse2dTransformer.Transform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, ref config, workspace, transformFunctionParameters.BitDepth);
}
}

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

@ -40,7 +40,7 @@ internal static class Av1InverseWalshHadamardTransformer
int writeStride,
int coefficientCount,
Span<int> workspace)
=> TransformAdd<byte, Av1InverseTransformer.OutputOperator<byte>>(
=> TransformAdd<byte, Av1InverseTransformOutputOperator<byte>>(
coefficients,
readBuffer,
readStride,
@ -70,7 +70,7 @@ internal static class Av1InverseWalshHadamardTransformer
int coefficientCount,
Span<int> workspace,
int bitDepth)
=> TransformAdd<short, Av1InverseTransformer.OutputOperator<short>>(
=> TransformAdd<short, Av1InverseTransformOutputOperator<short>>(
coefficients,
readBuffer,
readStride,
@ -93,7 +93,7 @@ internal static class Av1InverseWalshHadamardTransformer
Span<int> workspace,
int bitDepth)
where TSample : unmanaged
where TOutputOperator : struct, Av1InverseTransformer.IAv1InverseTransformOutputOperator<TSample>
where TOutputOperator : struct, IAv1InverseTransformOutputOperator<TSample>
{
if (Vector128.IsHardwareAccelerated)
{
@ -116,7 +116,7 @@ internal static class Av1InverseWalshHadamardTransformer
int coefficientCount,
int bitDepth)
where TSample : unmanaged
where TOutputOperator : struct, Av1InverseTransformer.IAv1InverseTransformOutputOperator<TSample>
where TOutputOperator : struct, IAv1InverseTransformOutputOperator<TSample>
{
ref int coefficientBase = ref MemoryMarshal.GetReference(coefficients);
Vector128<int> row0;
@ -172,7 +172,7 @@ internal static class Av1InverseWalshHadamardTransformer
Span<int> workspace,
int bitDepth)
where TSample : unmanaged
where TOutputOperator : struct, Av1InverseTransformer.IAv1InverseTransformOutputOperator<TSample>
where TOutputOperator : struct, IAv1InverseTransformOutputOperator<TSample>
{
ref TSample readBase = ref MemoryMarshal.GetReference(readBuffer);
ref TSample writeBase = ref MemoryMarshal.GetReference(writeBuffer);
@ -300,7 +300,7 @@ internal static class Av1InverseWalshHadamardTransformer
Vector128<int> row3,
int bitDepth)
where TSample : unmanaged
where TOutputOperator : struct, Av1InverseTransformer.IAv1InverseTransformOutputOperator<TSample>
where TOutputOperator : struct, IAv1InverseTransformOutputOperator<TSample>
{
ref TSample readBase = ref MemoryMarshal.GetReference(readBuffer);
ref TSample writeBase = ref MemoryMarshal.GetReference(writeBuffer);

5
src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform1dMath.cs

@ -17,6 +17,11 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// </remarks>
internal static class Av1Transform1dMath
{
/// <summary>
/// The signed stage width whose fixed-point terminal operations require widened SIMD intermediates.
/// </summary>
public const byte WidenedIntermediateBitCount = 20;
/// <summary>
/// The fixed-point representation of the square root of two with twelve fractional bits.
/// </summary>

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst16Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the sixteen-point AV1 forward asymmetric discrete sine transform operator.
/// </summary>
internal readonly struct Av1Adst16Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Adst16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst4Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the four-point AV1 forward asymmetric discrete sine transform operator.
/// </summary>
internal readonly struct Av1Adst4Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Adst4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst8Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the eight-point AV1 forward asymmetric discrete sine transform operator.
/// </summary>
internal readonly struct Av1Adst8Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Adst8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct16Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the sixteen-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Av1Dct16Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct32Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the thirty-two-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Av1Dct32Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct32(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct4Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the four-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Av1Dct4Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct64Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the sixty-four-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Av1Dct64Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct64(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct8Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the eight-point AV1 forward discrete cosine transform operator.
/// </summary>
internal readonly struct Av1Dct8Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Dct8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity16Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the sixteen-point AV1 forward identity transform operator.
/// </summary>
internal readonly struct Av1Identity16Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Identity16(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity32Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the thirty-two-point AV1 forward identity transform operator.
/// </summary>
internal readonly struct Av1Identity32Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Identity32(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity4Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the four-point AV1 forward identity transform operator.
/// </summary>
internal readonly struct Av1Identity4Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Identity4(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

21
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity8Forward1dOperator.cs

@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines the eight-point AV1 forward identity transform operator.
/// </summary>
internal readonly struct Av1Identity8Forward1dOperator : IAv1ForwardTransform1dOperator
{
/// <inheritdoc/>
public static void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct
=> Av1ForwardTransformOperations.Identity8(ref values, inputStride, outputStride, ref buffer0, ref buffer1, cosBit);
}

34
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/IAv1ForwardTransform1dOperator.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Defines one AV1 forward transform which can be specialized for the selected sample and SIMD lane type.
/// </summary>
/// <remarks>
/// A concrete operator identifies the transform stage network. The two-dimensional driver selects the sample type
/// and vector width once per block, allowing the JIT to specialize the complete network without interface dispatch
/// inside the transform stages.
/// </remarks>
internal interface IAv1ForwardTransform1dOperator
{
/// <summary>
/// Transforms the independent axes stored in each value lane.
/// </summary>
/// <typeparam name="TValue">The scalar or SIMD value containing the independent transform axes.</typeparam>
/// <param name="values">The first value in the strided transform block.</param>
/// <param name="inputStride">The byte distance between consecutive input positions.</param>
/// <param name="outputStride">The byte distance between consecutive output positions.</param>
/// <param name="buffer0">The first fixed transform-stage buffer.</param>
/// <param name="buffer1">The second fixed transform-stage buffer.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
public static abstract void Transform<TValue>(
ref byte values,
nint inputStride,
nint outputStride,
ref Av1TransformVector<TValue> buffer0,
ref Av1TransformVector<TValue> buffer1,
int cosBit)
where TValue : struct;
}

46
src/ImageSharp/Formats/Heif/Av1/Transform/IAv1InverseTransformOutputOperator.cs

@ -0,0 +1,46 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Defines how inverse-transform residuals are added to a decoded sample representation.
/// </summary>
/// <remarks>
/// Residual lanes correspond to consecutive reconstructed samples. Implementations must widen packed predictions,
/// add and clip in signed 32-bit lanes, then store exactly four or eight results so callers do not require writable
/// padding beyond the transform block. The closed sample type allows byte and high-bit-depth storage to specialize.
/// </remarks>
/// <typeparam name="TSample">The decoded sample storage type.</typeparam>
internal interface IAv1InverseTransformOutputOperator<TSample>
where TSample : unmanaged
{
/// <summary>
/// Adds one residual to a predicted sample and clips the result to the coded bit depth.
/// </summary>
/// <param name="prediction">The predicted sample.</param>
/// <param name="residual">The inverse-transform residual.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <returns>The reconstructed sample.</returns>
public static abstract TSample Add(TSample prediction, int residual, int bitDepth);
/// <summary>
/// Adds four residuals to four predicted samples and stores the clipped results.
/// </summary>
/// <param name="prediction">The first predicted sample.</param>
/// <param name="destination">The first destination sample.</param>
/// <param name="residual">The four inverse-transform residuals.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
public static abstract void Add(ref TSample prediction, ref TSample destination, Vector128<int> residual, int bitDepth);
/// <summary>
/// Adds eight residuals to eight predicted samples and stores the clipped results.
/// </summary>
/// <param name="prediction">The first predicted sample.</param>
/// <param name="destination">The first destination sample.</param>
/// <param name="residual">The eight inverse-transform residuals.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
public static abstract void Add(ref TSample prediction, ref TSample destination, Vector256<int> residual, int bitDepth);
}

59
src/ImageSharp/Formats/Heif/Av1/Transform/IAv1Transform1dOperator.cs

@ -0,0 +1,59 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Defines the scalar and SIMD arithmetic for one AV1 one-dimensional transform.
/// </summary>
/// <remarks>
/// Each overload performs the same staged fixed-point transform. In the SIMD overloads, each vector field identifies
/// one coefficient position and each lane identifies an independent row or column. Butterfly arithmetic is therefore
/// lane-local: vectorization changes only how many axes advance together, not coefficient order, rounding, or stage
/// clamping. The two-dimensional traversal selects the concrete operator and lane width once per block, allowing the
/// JIT to specialize every static interface call outside the stage network.
/// </remarks>
internal interface IAv1Transform1dOperator
{
/// <summary>
/// Transforms one axis when hardware vectorization is unavailable.
/// </summary>
/// <param name="input">The source values for the transform axis.</param>
/// <param name="output">The destination values for the transform axis.</param>
/// <param name="step">The fixed stage storage for the transform axis.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static abstract void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange);
/// <summary>
/// Transforms four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for four transform axes.</param>
/// <param name="output">The destination values for four transform axes.</param>
/// <param name="step">The fixed stage storage for four transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static abstract void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange);
/// <summary>
/// Transforms eight independent axes in parallel.
/// </summary>
/// <param name="input">The source values for eight transform axes.</param>
/// <param name="output">The destination values for eight transform axes.</param>
/// <param name="step">The fixed stage storage for eight transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static abstract void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange);
}

568
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst16Inverse1dOperator.cs

@ -0,0 +1,568 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the 16-point AV1 inverse asymmetric discrete sine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Av1Adst16Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative 16-point AV1 inverse asymmetric discrete sine transform.
/// </summary>
/// <param name="input">The sixteen frequency-domain coefficients.</param>
/// <param name="output">The sixteen spatial-domain residual values.</param>
/// <param name="step">The sixteen-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output[0] = input[15];
output[1] = input[0];
output[2] = input[13];
output[3] = input[2];
output[4] = input[11];
output[5] = input[4];
output[6] = input[9];
output[7] = input[6];
output[8] = input[7];
output[9] = input[8];
output[10] = input[5];
output[11] = input[10];
output[12] = input[3];
output[13] = input[12];
output[14] = input[1];
output[15] = input[14];
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step[0] = Av1Transform1dMath.HalfButterfly(cospi[2], output[0], cospi[62], output[1], cosBit);
step[1] = Av1Transform1dMath.HalfButterfly(cospi[62], output[0], -cospi[2], output[1], cosBit);
step[2] = Av1Transform1dMath.HalfButterfly(cospi[10], output[2], cospi[54], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[54], output[2], -cospi[10], output[3], cosBit);
step[4] = Av1Transform1dMath.HalfButterfly(cospi[18], output[4], cospi[46], output[5], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[46], output[4], -cospi[18], output[5], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(cospi[26], output[6], cospi[38], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[38], output[6], -cospi[26], output[7], cosBit);
step[8] = Av1Transform1dMath.HalfButterfly(cospi[34], output[8], cospi[30], output[9], cosBit);
step[9] = Av1Transform1dMath.HalfButterfly(cospi[30], output[8], -cospi[34], output[9], cosBit);
step[10] = Av1Transform1dMath.HalfButterfly(cospi[42], output[10], cospi[22], output[11], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(cospi[22], output[10], -cospi[42], output[11], cosBit);
step[12] = Av1Transform1dMath.HalfButterfly(cospi[50], output[12], cospi[14], output[13], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[14], output[12], -cospi[50], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(cospi[58], output[14], cospi[6], output[15], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[6], output[14], -cospi[58], output[15], cosBit);
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[8], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[9], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[10], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[11], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[12], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[5] + step[13], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[6] + step[14], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[7] + step[15], stageRange[stage]);
output[8] = Av1Transform1dMath.Clamp(step[0] - step[8], stageRange[stage]);
output[9] = Av1Transform1dMath.Clamp(step[1] - step[9], stageRange[stage]);
output[10] = Av1Transform1dMath.Clamp(step[2] - step[10], stageRange[stage]);
output[11] = Av1Transform1dMath.Clamp(step[3] - step[11], stageRange[stage]);
output[12] = Av1Transform1dMath.Clamp(step[4] - step[12], stageRange[stage]);
output[13] = Av1Transform1dMath.Clamp(step[5] - step[13], stageRange[stage]);
output[14] = Av1Transform1dMath.Clamp(step[6] - step[14], stageRange[stage]);
output[15] = Av1Transform1dMath.Clamp(step[7] - step[15], stageRange[stage]);
// Stage 4 reverses the pi/16 rotations in the upper half.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = output[4];
step[5] = output[5];
step[6] = output[6];
step[7] = output[7];
step[8] = Av1Transform1dMath.HalfButterfly(cospi[8], output[8], cospi[56], output[9], cosBit);
step[9] = Av1Transform1dMath.HalfButterfly(cospi[56], output[8], -cospi[8], output[9], cosBit);
step[10] = Av1Transform1dMath.HalfButterfly(cospi[40], output[10], cospi[24], output[11], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(cospi[24], output[10], -cospi[40], output[11], cosBit);
step[12] = Av1Transform1dMath.HalfButterfly(-cospi[56], output[12], cospi[8], output[13], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[8], output[12], cospi[56], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(-cospi[24], output[14], cospi[40], output[15], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[40], output[14], cospi[24], output[15], cosBit);
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[4], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[5], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[6], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[7], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[0] - step[4], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[1] - step[5], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[2] - step[6], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[3] - step[7], stageRange[stage]);
output[8] = Av1Transform1dMath.Clamp(step[8] + step[12], stageRange[stage]);
output[9] = Av1Transform1dMath.Clamp(step[9] + step[13], stageRange[stage]);
output[10] = Av1Transform1dMath.Clamp(step[10] + step[14], stageRange[stage]);
output[11] = Av1Transform1dMath.Clamp(step[11] + step[15], stageRange[stage]);
output[12] = Av1Transform1dMath.Clamp(step[8] - step[12], stageRange[stage]);
output[13] = Av1Transform1dMath.Clamp(step[9] - step[13], stageRange[stage]);
output[14] = Av1Transform1dMath.Clamp(step[10] - step[14], stageRange[stage]);
output[15] = Av1Transform1dMath.Clamp(step[11] - step[15], stageRange[stage]);
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = Av1Transform1dMath.HalfButterfly(cospi[16], output[4], cospi[48], output[5], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[48], output[4], -cospi[16], output[5], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[6], cospi[16], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[16], output[6], cospi[48], output[7], cosBit);
step[8] = output[8];
step[9] = output[9];
step[10] = output[10];
step[11] = output[11];
step[12] = Av1Transform1dMath.HalfButterfly(cospi[16], output[12], cospi[48], output[13], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[48], output[12], -cospi[16], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[14], cospi[16], output[15], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[16], output[14], cospi[48], output[15], cosBit);
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[2], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[3], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[0] - step[2], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[1] - step[3], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[6], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[5] + step[7], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[4] - step[6], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[5] - step[7], stageRange[stage]);
output[8] = Av1Transform1dMath.Clamp(step[8] + step[10], stageRange[stage]);
output[9] = Av1Transform1dMath.Clamp(step[9] + step[11], stageRange[stage]);
output[10] = Av1Transform1dMath.Clamp(step[8] - step[10], stageRange[stage]);
output[11] = Av1Transform1dMath.Clamp(step[9] - step[11], stageRange[stage]);
output[12] = Av1Transform1dMath.Clamp(step[12] + step[14], stageRange[stage]);
output[13] = Av1Transform1dMath.Clamp(step[13] + step[15], stageRange[stage]);
output[14] = Av1Transform1dMath.Clamp(step[12] - step[14], stageRange[stage]);
output[15] = Av1Transform1dMath.Clamp(step[13] - step[15], stageRange[stage]);
// Stage 8 reverses the pi/4 rotations for the middle pairs.
step[0] = output[0];
step[1] = output[1];
step[2] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], cospi[32], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], -cospi[32], output[3], cosBit);
step[4] = output[4];
step[5] = output[5];
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], cospi[32], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], -cospi[32], output[7], cosBit);
step[8] = output[8];
step[9] = output[9];
step[10] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], cospi[32], output[11], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], -cospi[32], output[11], cosBit);
step[12] = output[12];
step[13] = output[13];
step[14] = Av1Transform1dMath.HalfButterfly(cospi[32], output[14], cospi[32], output[15], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[32], output[14], -cospi[32], output[15], cosBit);
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
output[0] = step[0];
output[1] = -step[8];
output[2] = step[12];
output[3] = -step[4];
output[4] = step[6];
output[5] = -step[14];
output[6] = step[10];
output[7] = -step[2];
output[8] = step[3];
output[9] = -step[11];
output[10] = step[15];
output[11] = -step[7];
output[12] = step[5];
output[13] = -step[13];
output[14] = step[9];
output[15] = -step[1];
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output.V0 = input.V15;
output.V1 = input.V0;
output.V2 = input.V13;
output.V3 = input.V2;
output.V4 = input.V11;
output.V5 = input.V4;
output.V6 = input.V9;
output.V7 = input.V6;
output.V8 = input.V7;
output.V9 = input.V8;
output.V10 = input.V5;
output.V11 = input.V10;
output.V12 = input.V3;
output.V13 = input.V12;
output.V14 = input.V1;
output.V15 = input.V14;
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[2], output.V0, cospi[62], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[62], output.V0, -cospi[2], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[10], output.V2, cospi[54], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[54], output.V2, -cospi[10], output.V3, cosBit);
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[18], output.V4, cospi[46], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[46], output.V4, -cospi[18], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[26], output.V6, cospi[38], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[38], output.V6, -cospi[26], output.V7, cosBit);
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[34], output.V8, cospi[30], output.V9, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[30], output.V8, -cospi[34], output.V9, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[42], output.V10, cospi[22], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[22], output.V10, -cospi[42], output.V11, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[50], output.V12, cospi[14], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[14], output.V12, -cospi[50], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[58], output.V14, cospi[6], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[6], output.V14, -cospi[58], output.V15, cosBit);
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V8, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V9, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V10, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V11, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V12, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V13, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V14, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V15, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V0 - step.V8, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V1 - step.V9, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V2 - step.V10, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V3 - step.V11, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V4 - step.V12, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V5 - step.V13, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V6 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V7 - step.V15, stageRange[stage]);
// Stage 4 reverses the pi/16 rotations in the upper half.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = output.V6;
step.V7 = output.V7;
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V8, cospi[56], output.V9, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V8, -cospi[8], output.V9, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V10, cospi[24], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V10, -cospi[40], output.V11, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(-cospi[56], output.V12, cospi[8], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V12, cospi[56], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[24], output.V14, cospi[40], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V14, cospi[24], output.V15, cosBit);
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V12, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V13, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V10 + step.V14, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V11 + step.V15, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V8 - step.V12, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V9 - step.V13, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V10 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V11 - step.V15, stageRange[stage]);
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = output.V10;
step.V11 = output.V11;
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V12, cospi[48], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V12, -cospi[16], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V14, cospi[16], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V14, cospi[48], output.V15, cosBit);
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V10, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V11, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V8 - step.V10, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V9 - step.V11, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V14, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V13 + step.V15, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V12 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V13 - step.V15, stageRange[stage]);
// Stage 8 reverses the pi/4 rotations for the middle pairs.
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit);
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, -cospi[32], output.V11, cosBit);
step.V12 = output.V12;
step.V13 = output.V13;
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, cospi[32], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, -cospi[32], output.V15, cosBit);
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
output.V0 = step.V0;
output.V1 = -step.V8;
output.V2 = step.V12;
output.V3 = -step.V4;
output.V4 = step.V6;
output.V5 = -step.V14;
output.V6 = step.V10;
output.V7 = -step.V2;
output.V8 = step.V3;
output.V9 = -step.V11;
output.V10 = step.V15;
output.V11 = -step.V7;
output.V12 = step.V5;
output.V13 = -step.V13;
output.V14 = step.V9;
output.V15 = -step.V1;
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output.V0 = input.V15;
output.V1 = input.V0;
output.V2 = input.V13;
output.V3 = input.V2;
output.V4 = input.V11;
output.V5 = input.V4;
output.V6 = input.V9;
output.V7 = input.V6;
output.V8 = input.V7;
output.V9 = input.V8;
output.V10 = input.V5;
output.V11 = input.V10;
output.V12 = input.V3;
output.V13 = input.V12;
output.V14 = input.V1;
output.V15 = input.V14;
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[2], output.V0, cospi[62], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[62], output.V0, -cospi[2], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[10], output.V2, cospi[54], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[54], output.V2, -cospi[10], output.V3, cosBit);
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[18], output.V4, cospi[46], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[46], output.V4, -cospi[18], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[26], output.V6, cospi[38], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[38], output.V6, -cospi[26], output.V7, cosBit);
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[34], output.V8, cospi[30], output.V9, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[30], output.V8, -cospi[34], output.V9, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[42], output.V10, cospi[22], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[22], output.V10, -cospi[42], output.V11, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[50], output.V12, cospi[14], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[14], output.V12, -cospi[50], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[58], output.V14, cospi[6], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[6], output.V14, -cospi[58], output.V15, cosBit);
// Stage 3 separates the complete butterfly into two eight-sample halves and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V8, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V9, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V10, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V11, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V12, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V13, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V14, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V15, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V0 - step.V8, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V1 - step.V9, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V2 - step.V10, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V3 - step.V11, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V4 - step.V12, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V5 - step.V13, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V6 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V7 - step.V15, stageRange[stage]);
// Stage 4 reverses the pi/16 rotations in the upper half.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = output.V6;
step.V7 = output.V7;
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V8, cospi[56], output.V9, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V8, -cospi[8], output.V9, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V10, cospi[24], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V10, -cospi[40], output.V11, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(-cospi[56], output.V12, cospi[8], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V12, cospi[56], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[24], output.V14, cospi[40], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V14, cospi[24], output.V15, cosBit);
// Stage 5 separates each eight-sample half into four-sample groups and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V12, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V13, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V10 + step.V14, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V11 + step.V15, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V8 - step.V12, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V9 - step.V13, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V10 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V11 - step.V15, stageRange[stage]);
// Stage 6 reverses the pi/8 and 3pi/8 rotations.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = output.V10;
step.V11 = output.V11;
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V12, cospi[48], output.V13, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V12, -cospi[16], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V14, cospi[16], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V14, cospi[48], output.V15, cosBit);
// Stage 7 separates the four-sample groups into adjacent coefficient pairs and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V10, stageRange[stage]);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V11, stageRange[stage]);
output.V10 = Av1Transform1dMath.Clamp(step.V8 - step.V10, stageRange[stage]);
output.V11 = Av1Transform1dMath.Clamp(step.V9 - step.V11, stageRange[stage]);
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V14, stageRange[stage]);
output.V13 = Av1Transform1dMath.Clamp(step.V13 + step.V15, stageRange[stage]);
output.V14 = Av1Transform1dMath.Clamp(step.V12 - step.V14, stageRange[stage]);
output.V15 = Av1Transform1dMath.Clamp(step.V13 - step.V15, stageRange[stage]);
// Stage 8 reverses the pi/4 rotations for the middle pairs.
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit);
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V11, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, -cospi[32], output.V11, cosBit);
step.V12 = output.V12;
step.V13 = output.V13;
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, cospi[32], output.V15, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V14, -cospi[32], output.V15, cosBit);
// Stage 9 applies the AV1 signs and permutation that restore spatial sample order.
output.V0 = step.V0;
output.V1 = -step.V8;
output.V2 = step.V12;
output.V3 = -step.V4;
output.V4 = step.V6;
output.V5 = -step.V14;
output.V6 = step.V10;
output.V7 = -step.V2;
output.V8 = step.V3;
output.V9 = -step.V11;
output.V10 = step.V15;
output.V11 = -step.V7;
output.V12 = step.V5;
output.V13 = -step.V13;
output.V14 = step.V9;
output.V15 = -step.V1;
}
}

168
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dOperator.cs

@ -0,0 +1,168 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the four-point AV1 inverse asymmetric discrete sine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Av1Adst4Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative four-point AV1 inverse asymmetric discrete sine transform.
/// </summary>
/// <param name="input">The four frequency-domain coefficients.</param>
/// <param name="output">The four spatial-domain residual values.</param>
/// <param name="step">The stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit);
// libaom widens the complete four-point factorization because the products retain their fixed-point scale
// until the final shift. The stage buffer is therefore unnecessary for this transform size.
long x0 = input[0];
long x1 = input[1];
long x2 = input[2];
long x3 = input[3];
_ = step;
_ = stageRange;
// Avoid the multiplications for the all-zero coefficient vector, matching libaom's scalar kernel.
if ((x0 | x1 | x2 | x3) == 0)
{
output[..4].Clear();
return;
}
// Stages 1 and 2 form the seven sine products and the one unscaled combination used by stage 3.
long s0 = sinpi[1] * x0;
long s1 = sinpi[2] * x0;
long s2 = sinpi[3] * x1;
long s3 = sinpi[4] * x2;
long s4 = sinpi[1] * x2;
long s5 = sinpi[2] * x3;
long s6 = sinpi[4] * x3;
long s7 = (x0 - x2) + x3;
// Stages 3 through 6 combine the products while preserving the fixed-point scale until the final rounding.
s0 += s3;
s1 -= s4;
s3 = s2;
s2 = sinpi[3] * s7;
s0 += s5;
s1 -= s6;
x0 = s0 + s3;
x1 = s1 + s3;
x2 = s2;
x3 = (s0 + s1) - s3;
output[0] = Av1Math.RoundShift(x0, cosBit);
output[1] = Av1Math.RoundShift(x1, cosBit);
output[2] = Av1Math.RoundShift(x2, cosBit);
output[3] = Av1Math.RoundShift(x3, cosBit);
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
TransformCore(ref input, ref output, cosBit, stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount);
_ = step;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
TransformCore(ref input, ref output, cosBit, stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount);
_ = step;
}
/// <summary>
/// Applies the inverse four-point matrix to four independent axes.
/// </summary>
/// <param name="input">The source values for four transform axes.</param>
/// <param name="output">The destination values for four transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
/// <param name="widenedRound">Whether the terminal fixed-point rounding requires signed 64-bit lanes.</param>
private static void TransformCore(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
int cosBit,
bool widenedRound)
{
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit);
Vector128<int> x0 = input.V0;
Vector128<int> x1 = input.V1;
Vector128<int> x2 = input.V2;
Vector128<int> x3 = input.V3;
// Pinned libaom retains the sine-table scale in Int32 products and sums, but performs the twelve-bit row
// kernel's terminal scaling and rounding in Int64. This is the only stage whose rounding bias can overflow
// a valid Int32 fixed-point sum.
if (widenedRound)
{
output.V0 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit);
output.V1 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit);
output.V2 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit);
output.V3 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit);
return;
}
output.V0 = Av1Transform1dMath.MultiplyAdd4(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit);
output.V1 = Av1Transform1dMath.MultiplyAdd4(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit);
output.V2 = Av1Transform1dMath.MultiplyAdd4(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit);
output.V3 = Av1Transform1dMath.MultiplyAdd4(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit);
}
/// <summary>
/// Applies the inverse four-point matrix to eight independent axes.
/// </summary>
/// <param name="input">The source values for eight transform axes.</param>
/// <param name="output">The destination values for eight transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the sine constants.</param>
/// <param name="widenedRound">Whether the terminal fixed-point rounding requires signed 64-bit lanes.</param>
private static void TransformCore(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
int cosBit,
bool widenedRound)
{
ReadOnlySpan<int> sinpi = Av1SinusConstants.SinusPi(cosBit);
Vector256<int> x0 = input.V0;
Vector256<int> x1 = input.V1;
Vector256<int> x2 = input.V2;
Vector256<int> x3 = input.V3;
if (widenedRound)
{
output.V0 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit);
output.V1 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit);
output.V2 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit);
output.V3 = Av1Transform1dMath.MultiplyAdd4WidenedRound(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit);
return;
}
output.V0 = Av1Transform1dMath.MultiplyAdd4(sinpi[1], x0, sinpi[3], x1, sinpi[4], x2, sinpi[2], x3, cosBit);
output.V1 = Av1Transform1dMath.MultiplyAdd4(sinpi[2], x0, sinpi[3], x1, -sinpi[1], x2, -sinpi[4], x3, cosBit);
output.V2 = Av1Transform1dMath.MultiplyAdd4(sinpi[3], x0, 0, x1, -sinpi[3], x2, sinpi[3], x3, cosBit);
output.V3 = Av1Transform1dMath.MultiplyAdd4(sinpi[1] + sinpi[2], x0, -sinpi[3], x1, sinpi[4] - sinpi[1], x2, sinpi[2] - sinpi[4], x3, cosBit);
}
}

289
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst8Inverse1dOperator.cs

@ -0,0 +1,289 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the eight-point AV1 inverse asymmetric discrete sine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged rotations, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Av1Adst8Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative eight-point AV1 inverse asymmetric discrete sine transform.
/// </summary>
/// <param name="input">The eight frequency-domain coefficients.</param>
/// <param name="output">The eight spatial-domain residual values.</param>
/// <param name="step">The eight-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output[0] = input[7];
output[1] = input[0];
output[2] = input[5];
output[3] = input[2];
output[4] = input[3];
output[5] = input[4];
output[6] = input[1];
output[7] = input[6];
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step[0] = Av1Transform1dMath.HalfButterfly(cospi[4], output[0], cospi[60], output[1], cosBit);
step[1] = Av1Transform1dMath.HalfButterfly(cospi[60], output[0], -cospi[4], output[1], cosBit);
step[2] = Av1Transform1dMath.HalfButterfly(cospi[20], output[2], cospi[44], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[44], output[2], -cospi[20], output[3], cosBit);
step[4] = Av1Transform1dMath.HalfButterfly(cospi[36], output[4], cospi[28], output[5], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[28], output[4], -cospi[36], output[5], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(cospi[52], output[6], cospi[12], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[12], output[6], -cospi[52], output[7], cosBit);
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[4], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[5], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[6], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[7], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[0] - step[4], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[1] - step[5], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[2] - step[6], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[3] - step[7], stageRange[stage]);
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = Av1Transform1dMath.HalfButterfly(cospi[16], output[4], cospi[48], output[5], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[48], output[4], -cospi[16], output[5], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[6], cospi[16], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[16], output[6], cospi[48], output[7], cosBit);
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
stage++;
output[0] = Av1Transform1dMath.Clamp(step[0] + step[2], stageRange[stage]);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[3], stageRange[stage]);
output[2] = Av1Transform1dMath.Clamp(step[0] - step[2], stageRange[stage]);
output[3] = Av1Transform1dMath.Clamp(step[1] - step[3], stageRange[stage]);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[6], stageRange[stage]);
output[5] = Av1Transform1dMath.Clamp(step[5] + step[7], stageRange[stage]);
output[6] = Av1Transform1dMath.Clamp(step[4] - step[6], stageRange[stage]);
output[7] = Av1Transform1dMath.Clamp(step[5] - step[7], stageRange[stage]);
// Stage 6 reverses the pi/4 rotations for the middle pairs.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], cospi[32], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[32], output[2], -cospi[32], output[3], cosBit);
step[4] = output[4];
step[5] = output[5];
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], cospi[32], output[7], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[32], output[6], -cospi[32], output[7], cosBit);
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
output[0] = step[0];
output[1] = -step[4];
output[2] = step[6];
output[3] = -step[2];
output[4] = step[3];
output[5] = -step[7];
output[6] = step[5];
output[7] = -step[1];
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output.V0 = input.V7;
output.V1 = input.V0;
output.V2 = input.V5;
output.V3 = input.V2;
output.V4 = input.V3;
output.V5 = input.V4;
output.V6 = input.V1;
output.V7 = input.V6;
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V0, cospi[60], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V0, -cospi[4], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V2, cospi[44], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V2, -cospi[20], output.V3, cosBit);
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V4, cospi[28], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V4, -cospi[36], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V6, cospi[12], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V6, -cospi[52], output.V7, cosBit);
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]);
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit);
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]);
// Stage 6 reverses the pi/4 rotations for the middle pairs.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit);
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit);
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
output.V0 = step.V0;
output.V1 = -step.V4;
output.V2 = step.V6;
output.V3 = -step.V2;
output.V4 = step.V3;
output.V5 = -step.V7;
output.V6 = step.V5;
output.V7 = -step.V1;
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes the coefficients into the signed order used by the ADST factorization.
stage++;
output.V0 = input.V7;
output.V1 = input.V0;
output.V2 = input.V5;
output.V3 = input.V2;
output.V4 = input.V3;
output.V5 = input.V4;
output.V6 = input.V1;
output.V7 = input.V6;
// Stage 2 applies the terminal odd-angle rotations in reverse.
stage++;
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V0, cospi[60], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V0, -cospi[4], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V2, cospi[44], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V2, -cospi[20], output.V3, cosBit);
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V4, cospi[28], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V4, -cospi[36], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V6, cospi[12], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V6, -cospi[52], output.V7, cosBit);
// Stage 3 separates the complete butterfly into two four-sample halves and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V4, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V5, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V6, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V7, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V0 - step.V4, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V1 - step.V5, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V2 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V3 - step.V7, stageRange[stage]);
// Stage 4 reverses the pi/8 and 3pi/8 rotations in the upper half.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V4, cospi[48], output.V5, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V4, -cospi[16], output.V5, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V6, cospi[16], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V6, cospi[48], output.V7, cosBit);
// Stage 5 separates the four-sample halves into adjacent coefficient pairs and clamps each lane.
stage++;
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V2, stageRange[stage]);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V3, stageRange[stage]);
output.V2 = Av1Transform1dMath.Clamp(step.V0 - step.V2, stageRange[stage]);
output.V3 = Av1Transform1dMath.Clamp(step.V1 - step.V3, stageRange[stage]);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V6, stageRange[stage]);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V7, stageRange[stage]);
output.V6 = Av1Transform1dMath.Clamp(step.V4 - step.V6, stageRange[stage]);
output.V7 = Av1Transform1dMath.Clamp(step.V5 - step.V7, stageRange[stage]);
// Stage 6 reverses the pi/4 rotations for the middle pairs.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, cospi[32], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V2, -cospi[32], output.V3, cosBit);
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, cospi[32], output.V7, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V6, -cospi[32], output.V7, cosBit);
// Stage 7 applies the AV1 signs and permutation that restore spatial sample order.
output.V0 = step.V0;
output.V1 = -step.V4;
output.V2 = step.V6;
output.V3 = -step.V2;
output.V4 = step.V3;
output.V5 = -step.V7;
output.V6 = step.V5;
output.V7 = -step.V1;
}
}

475
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct16Inverse1dOperator.cs

@ -0,0 +1,475 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the 16-point AV1 inverse discrete cosine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Av1Dct16Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative 16-point AV1 inverse discrete cosine transform.
/// </summary>
/// <param name="input">The sixteen frequency-domain coefficients.</param>
/// <param name="output">The sixteen spatial-domain residual values.</param>
/// <param name="step">The sixteen-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output[0] = input[0];
output[1] = input[8];
output[2] = input[4];
output[3] = input[12];
output[4] = input[2];
output[5] = input[10];
output[6] = input[6];
output[7] = input[14];
output[8] = input[1];
output[9] = input[9];
output[10] = input[5];
output[11] = input[13];
output[12] = input[3];
output[13] = input[11];
output[14] = input[7];
output[15] = input[15];
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = output[4];
step[5] = output[5];
step[6] = output[6];
step[7] = output[7];
step[8] = Av1Transform1dMath.HalfButterfly(cospi[60], output[8], -cospi[4], output[15], cosBit);
step[9] = Av1Transform1dMath.HalfButterfly(cospi[28], output[9], -cospi[36], output[14], cosBit);
step[10] = Av1Transform1dMath.HalfButterfly(cospi[44], output[10], -cospi[20], output[13], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(cospi[12], output[11], -cospi[52], output[12], cosBit);
step[12] = Av1Transform1dMath.HalfButterfly(cospi[52], output[11], cospi[12], output[12], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[20], output[10], cospi[44], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(cospi[36], output[9], cospi[28], output[14], cosBit);
step[15] = Av1Transform1dMath.HalfButterfly(cospi[4], output[8], cospi[60], output[15], cosBit);
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output[0] = step[0];
output[1] = step[1];
output[2] = step[2];
output[3] = step[3];
output[4] = Av1Transform1dMath.HalfButterfly(cospi[56], step[4], -cospi[8], step[7], cosBit);
output[5] = Av1Transform1dMath.HalfButterfly(cospi[24], step[5], -cospi[40], step[6], cosBit);
output[6] = Av1Transform1dMath.HalfButterfly(cospi[40], step[5], cospi[24], step[6], cosBit);
output[7] = Av1Transform1dMath.HalfButterfly(cospi[8], step[4], cospi[56], step[7], cosBit);
output[8] = Av1Transform1dMath.Clamp(step[8] + step[9], range);
output[9] = Av1Transform1dMath.Clamp(step[8] - step[9], range);
output[10] = Av1Transform1dMath.Clamp(step[11] - step[10], range);
output[11] = Av1Transform1dMath.Clamp(step[10] + step[11], range);
output[12] = Av1Transform1dMath.Clamp(step[12] + step[13], range);
output[13] = Av1Transform1dMath.Clamp(step[12] - step[13], range);
output[14] = Av1Transform1dMath.Clamp(step[15] - step[14], range);
output[15] = Av1Transform1dMath.Clamp(step[14] + step[15], range);
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
stage++;
range = stageRange[stage];
step[0] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], cospi[32], output[1], cosBit);
step[1] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], -cospi[32], output[1], cosBit);
step[2] = Av1Transform1dMath.HalfButterfly(cospi[48], output[2], -cospi[16], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[16], output[2], cospi[48], output[3], cosBit);
step[4] = Av1Transform1dMath.Clamp(output[4] + output[5], range);
step[5] = Av1Transform1dMath.Clamp(output[4] - output[5], range);
step[6] = Av1Transform1dMath.Clamp(output[7] - output[6], range);
step[7] = Av1Transform1dMath.Clamp(output[6] + output[7], range);
step[8] = output[8];
step[9] = Av1Transform1dMath.HalfButterfly(-cospi[16], output[9], cospi[48], output[14], cosBit);
step[10] = Av1Transform1dMath.HalfButterfly(-cospi[48], output[10], -cospi[16], output[13], cosBit);
step[11] = output[11];
step[12] = output[12];
step[13] = Av1Transform1dMath.HalfButterfly(-cospi[16], output[10], cospi[48], output[13], cosBit);
step[14] = Av1Transform1dMath.HalfButterfly(cospi[48], output[9], cospi[16], output[14], cosBit);
step[15] = output[15];
// Stage 5 widens the reconstructed groups through their next butterfly level.
stage++;
range = stageRange[stage];
output[0] = Av1Transform1dMath.Clamp(step[0] + step[3], range);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[2], range);
output[2] = Av1Transform1dMath.Clamp(step[1] - step[2], range);
output[3] = Av1Transform1dMath.Clamp(step[0] - step[3], range);
output[4] = step[4];
output[5] = Av1Transform1dMath.HalfButterfly(-cospi[32], step[5], cospi[32], step[6], cosBit);
output[6] = Av1Transform1dMath.HalfButterfly(cospi[32], step[5], cospi[32], step[6], cosBit);
output[7] = step[7];
output[8] = Av1Transform1dMath.Clamp(step[8] + step[11], range);
output[9] = Av1Transform1dMath.Clamp(step[9] + step[10], range);
output[10] = Av1Transform1dMath.Clamp(step[9] - step[10], range);
output[11] = Av1Transform1dMath.Clamp(step[8] - step[11], range);
output[12] = Av1Transform1dMath.Clamp(step[15] - step[12], range);
output[13] = Av1Transform1dMath.Clamp(step[14] - step[13], range);
output[14] = Av1Transform1dMath.Clamp(step[13] + step[14], range);
output[15] = Av1Transform1dMath.Clamp(step[12] + step[15], range);
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
stage++;
range = stageRange[stage];
step[0] = Av1Transform1dMath.Clamp(output[0] + output[7], range);
step[1] = Av1Transform1dMath.Clamp(output[1] + output[6], range);
step[2] = Av1Transform1dMath.Clamp(output[2] + output[5], range);
step[3] = Av1Transform1dMath.Clamp(output[3] + output[4], range);
step[4] = Av1Transform1dMath.Clamp(output[3] - output[4], range);
step[5] = Av1Transform1dMath.Clamp(output[2] - output[5], range);
step[6] = Av1Transform1dMath.Clamp(output[1] - output[6], range);
step[7] = Av1Transform1dMath.Clamp(output[0] - output[7], range);
step[8] = output[8];
step[9] = output[9];
step[10] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[10], cospi[32], output[13], cosBit);
step[11] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[11], cospi[32], output[12], cosBit);
step[12] = Av1Transform1dMath.HalfButterfly(cospi[32], output[11], cospi[32], output[12], cosBit);
step[13] = Av1Transform1dMath.HalfButterfly(cospi[32], output[10], cospi[32], output[13], cosBit);
step[14] = output[14];
step[15] = output[15];
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output[0] = Av1Transform1dMath.Clamp(step[0] + step[15], range);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[14], range);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[13], range);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[12], range);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[11], range);
output[5] = Av1Transform1dMath.Clamp(step[5] + step[10], range);
output[6] = Av1Transform1dMath.Clamp(step[6] + step[9], range);
output[7] = Av1Transform1dMath.Clamp(step[7] + step[8], range);
output[8] = Av1Transform1dMath.Clamp(step[7] - step[8], range);
output[9] = Av1Transform1dMath.Clamp(step[6] - step[9], range);
output[10] = Av1Transform1dMath.Clamp(step[5] - step[10], range);
output[11] = Av1Transform1dMath.Clamp(step[4] - step[11], range);
output[12] = Av1Transform1dMath.Clamp(step[3] - step[12], range);
output[13] = Av1Transform1dMath.Clamp(step[2] - step[13], range);
output[14] = Av1Transform1dMath.Clamp(step[1] - step[14], range);
output[15] = Av1Transform1dMath.Clamp(step[0] - step[15], range);
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output.V0 = input.V0;
output.V1 = input.V8;
output.V2 = input.V4;
output.V3 = input.V12;
output.V4 = input.V2;
output.V5 = input.V10;
output.V6 = input.V6;
output.V7 = input.V14;
output.V8 = input.V1;
output.V9 = input.V9;
output.V10 = input.V5;
output.V11 = input.V13;
output.V12 = input.V3;
output.V13 = input.V11;
output.V14 = input.V7;
output.V15 = input.V15;
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = output.V6;
step.V7 = output.V7;
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V8, -cospi[4], output.V15, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V9, -cospi[36], output.V14, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V10, -cospi[20], output.V13, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V11, -cospi[52], output.V12, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V11, cospi[12], output.V12, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V10, cospi[44], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V9, cospi[28], output.V14, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V8, cospi[60], output.V15, cosBit);
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output.V0 = step.V0;
output.V1 = step.V1;
output.V2 = step.V2;
output.V3 = step.V3;
output.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], step.V4, -cospi[8], step.V7, cosBit);
output.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], step.V5, -cospi[40], step.V6, cosBit);
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], step.V5, cospi[24], step.V6, cosBit);
output.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], step.V4, cospi[56], step.V7, cosBit);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V9, range);
output.V9 = Av1Transform1dMath.Clamp(step.V8 - step.V9, range);
output.V10 = Av1Transform1dMath.Clamp(step.V11 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V10 + step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V13, range);
output.V13 = Av1Transform1dMath.Clamp(step.V12 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V15 - step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V14 + step.V15, range);
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
stage++;
range = stageRange[stage];
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit);
step.V4 = Av1Transform1dMath.Clamp(output.V4 + output.V5, range);
step.V5 = Av1Transform1dMath.Clamp(output.V4 - output.V5, range);
step.V6 = Av1Transform1dMath.Clamp(output.V7 - output.V6, range);
step.V7 = Av1Transform1dMath.Clamp(output.V6 + output.V7, range);
step.V8 = output.V8;
step.V9 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V9, cospi[48], output.V14, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V10, -cospi[16], output.V13, cosBit);
step.V11 = output.V11;
step.V12 = output.V12;
step.V13 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V10, cospi[48], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V9, cospi[16], output.V14, cosBit);
step.V15 = output.V15;
// Stage 5 widens the reconstructed groups through their next butterfly level.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range);
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range);
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range);
output.V4 = step.V4;
output.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], step.V5, cospi[32], step.V6, cosBit);
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V5, cospi[32], step.V6, cosBit);
output.V7 = step.V7;
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V11, range);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V10, range);
output.V10 = Av1Transform1dMath.Clamp(step.V9 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V8 - step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V15 - step.V12, range);
output.V13 = Av1Transform1dMath.Clamp(step.V14 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V13 + step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V12 + step.V15, range);
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
stage++;
range = stageRange[stage];
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V7, range);
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V6, range);
step.V2 = Av1Transform1dMath.Clamp(output.V2 + output.V5, range);
step.V3 = Av1Transform1dMath.Clamp(output.V3 + output.V4, range);
step.V4 = Av1Transform1dMath.Clamp(output.V3 - output.V4, range);
step.V5 = Av1Transform1dMath.Clamp(output.V2 - output.V5, range);
step.V6 = Av1Transform1dMath.Clamp(output.V1 - output.V6, range);
step.V7 = Av1Transform1dMath.Clamp(output.V0 - output.V7, range);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V10, cospi[32], output.V13, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V11, cospi[32], output.V12, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V11, cospi[32], output.V12, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V13, cosBit);
step.V14 = output.V14;
step.V15 = output.V15;
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V15, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V14, range);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V13, range);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V12, range);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V11, range);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V10, range);
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V9, range);
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V8, range);
output.V8 = Av1Transform1dMath.Clamp(step.V7 - step.V8, range);
output.V9 = Av1Transform1dMath.Clamp(step.V6 - step.V9, range);
output.V10 = Av1Transform1dMath.Clamp(step.V5 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V4 - step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V3 - step.V12, range);
output.V13 = Av1Transform1dMath.Clamp(step.V2 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V1 - step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V0 - step.V15, range);
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output.V0 = input.V0;
output.V1 = input.V8;
output.V2 = input.V4;
output.V3 = input.V12;
output.V4 = input.V2;
output.V5 = input.V10;
output.V6 = input.V6;
output.V7 = input.V14;
output.V8 = input.V1;
output.V9 = input.V9;
output.V10 = input.V5;
output.V11 = input.V13;
output.V12 = input.V3;
output.V13 = input.V11;
output.V14 = input.V7;
output.V15 = input.V15;
// Stage 2 rotates the highest odd-frequency coefficient pairs by their pi/32 angles.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = output.V4;
step.V5 = output.V5;
step.V6 = output.V6;
step.V7 = output.V7;
step.V8 = Av1Transform1dMath.HalfButterfly(cospi[60], output.V8, -cospi[4], output.V15, cosBit);
step.V9 = Av1Transform1dMath.HalfButterfly(cospi[28], output.V9, -cospi[36], output.V14, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(cospi[44], output.V10, -cospi[20], output.V13, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(cospi[12], output.V11, -cospi[52], output.V12, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[52], output.V11, cospi[12], output.V12, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[20], output.V10, cospi[44], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[36], output.V9, cospi[28], output.V14, cosBit);
step.V15 = Av1Transform1dMath.HalfButterfly(cospi[4], output.V8, cospi[60], output.V15, cosBit);
// Stage 3 reconstructs the embedded eight-point groups and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output.V0 = step.V0;
output.V1 = step.V1;
output.V2 = step.V2;
output.V3 = step.V3;
output.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], step.V4, -cospi[8], step.V7, cosBit);
output.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], step.V5, -cospi[40], step.V6, cosBit);
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], step.V5, cospi[24], step.V6, cosBit);
output.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], step.V4, cospi[56], step.V7, cosBit);
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V9, range);
output.V9 = Av1Transform1dMath.Clamp(step.V8 - step.V9, range);
output.V10 = Av1Transform1dMath.Clamp(step.V11 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V10 + step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V12 + step.V13, range);
output.V13 = Av1Transform1dMath.Clamp(step.V12 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V15 - step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V14 + step.V15, range);
// Stage 4 completes the low-frequency four-point DCT and rotates the next odd-frequency pairs.
stage++;
range = stageRange[stage];
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit);
step.V4 = Av1Transform1dMath.Clamp(output.V4 + output.V5, range);
step.V5 = Av1Transform1dMath.Clamp(output.V4 - output.V5, range);
step.V6 = Av1Transform1dMath.Clamp(output.V7 - output.V6, range);
step.V7 = Av1Transform1dMath.Clamp(output.V6 + output.V7, range);
step.V8 = output.V8;
step.V9 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V9, cospi[48], output.V14, cosBit);
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[48], output.V10, -cospi[16], output.V13, cosBit);
step.V11 = output.V11;
step.V12 = output.V12;
step.V13 = Av1Transform1dMath.HalfButterfly(-cospi[16], output.V10, cospi[48], output.V13, cosBit);
step.V14 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V9, cospi[16], output.V14, cosBit);
step.V15 = output.V15;
// Stage 5 widens the reconstructed groups through their next butterfly level.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range);
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range);
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range);
output.V4 = step.V4;
output.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], step.V5, cospi[32], step.V6, cosBit);
output.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V5, cospi[32], step.V6, cosBit);
output.V7 = step.V7;
output.V8 = Av1Transform1dMath.Clamp(step.V8 + step.V11, range);
output.V9 = Av1Transform1dMath.Clamp(step.V9 + step.V10, range);
output.V10 = Av1Transform1dMath.Clamp(step.V9 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V8 - step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V15 - step.V12, range);
output.V13 = Av1Transform1dMath.Clamp(step.V14 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V13 + step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V12 + step.V15, range);
// Stage 6 applies the remaining pi/4 rotations before the terminal spatial merge.
stage++;
range = stageRange[stage];
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V7, range);
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V6, range);
step.V2 = Av1Transform1dMath.Clamp(output.V2 + output.V5, range);
step.V3 = Av1Transform1dMath.Clamp(output.V3 + output.V4, range);
step.V4 = Av1Transform1dMath.Clamp(output.V3 - output.V4, range);
step.V5 = Av1Transform1dMath.Clamp(output.V2 - output.V5, range);
step.V6 = Av1Transform1dMath.Clamp(output.V1 - output.V6, range);
step.V7 = Av1Transform1dMath.Clamp(output.V0 - output.V7, range);
step.V8 = output.V8;
step.V9 = output.V9;
step.V10 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V10, cospi[32], output.V13, cosBit);
step.V11 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V11, cospi[32], output.V12, cosBit);
step.V12 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V11, cospi[32], output.V12, cosBit);
step.V13 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V10, cospi[32], output.V13, cosBit);
step.V14 = output.V14;
step.V15 = output.V15;
// Stage 7 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V15, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V14, range);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V13, range);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V12, range);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V11, range);
output.V5 = Av1Transform1dMath.Clamp(step.V5 + step.V10, range);
output.V6 = Av1Transform1dMath.Clamp(step.V6 + step.V9, range);
output.V7 = Av1Transform1dMath.Clamp(step.V7 + step.V8, range);
output.V8 = Av1Transform1dMath.Clamp(step.V7 - step.V8, range);
output.V9 = Av1Transform1dMath.Clamp(step.V6 - step.V9, range);
output.V10 = Av1Transform1dMath.Clamp(step.V5 - step.V10, range);
output.V11 = Av1Transform1dMath.Clamp(step.V4 - step.V11, range);
output.V12 = Av1Transform1dMath.Clamp(step.V3 - step.V12, range);
output.V13 = Av1Transform1dMath.Clamp(step.V2 - step.V13, range);
output.V14 = Av1Transform1dMath.Clamp(step.V1 - step.V14, range);
output.V15 = Av1Transform1dMath.Clamp(step.V0 - step.V15, range);
}
}

1027
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct32Inverse1dOperator.cs

File diff suppressed because it is too large

112
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dOperator.cs

@ -0,0 +1,112 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the four-point AV1 inverse discrete cosine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Av1Dct4Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative four-point AV1 inverse discrete cosine transform.
/// </summary>
/// <param name="input">The four frequency-domain coefficients.</param>
/// <param name="output">The four spatial-domain residual values.</param>
/// <param name="step">The four-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
output[0] = input[0];
output[1] = input[2];
output[2] = input[1];
output[3] = input[3];
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
step[0] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], cospi[32], output[1], cosBit);
step[1] = Av1Transform1dMath.HalfButterfly(cospi[32], output[0], -cospi[32], output[1], cosBit);
step[2] = Av1Transform1dMath.HalfButterfly(cospi[48], output[2], -cospi[16], output[3], cosBit);
step[3] = Av1Transform1dMath.HalfButterfly(cospi[16], output[2], cospi[48], output[3], cosBit);
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
byte range = stageRange[3];
output[0] = Av1Transform1dMath.Clamp(step[0] + step[3], range);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[2], range);
output[2] = Av1Transform1dMath.Clamp(step[1] - step[2], range);
output[3] = Av1Transform1dMath.Clamp(step[0] - step[3], range);
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
output.V0 = input.V0;
output.V1 = input.V2;
output.V2 = input.V1;
output.V3 = input.V3;
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit);
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
byte range = stageRange[3];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range);
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range);
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range);
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
// AV1 stores coefficients in frequency order; this permutation restores the order expected by the staged DCT.
output.V0 = input.V0;
output.V1 = input.V2;
output.V2 = input.V1;
output.V3 = input.V3;
// Rotate the even and odd coefficient pairs using the same fixed-point basis as the forward transform.
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
step.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, cospi[32], output.V1, cosBit);
step.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V0, -cospi[32], output.V1, cosBit);
step.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], output.V2, -cospi[16], output.V3, cosBit);
step.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], output.V2, cospi[48], output.V3, cosBit);
// The terminal butterflies reconstruct spatial order and clamp every result to the normative stage range.
byte range = stageRange[3];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V3, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V2, range);
output.V2 = Av1Transform1dMath.Clamp(step.V1 - step.V2, range);
output.V3 = Av1Transform1dMath.Clamp(step.V0 - step.V3, range);
}
}

2272
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct64Inverse1dOperator.cs

File diff suppressed because it is too large

232
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct8Inverse1dOperator.cs

@ -0,0 +1,232 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the eight-point AV1 inverse discrete cosine transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. The SIMD overloads apply
/// the same staged butterflies, fixed-point rounding, and range clamps as the scalar overload without mixing axes.
/// </remarks>
internal readonly struct Av1Dct8Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative eight-point AV1 inverse discrete cosine transform.
/// </summary>
/// <param name="input">The eight frequency-domain coefficients.</param>
/// <param name="output">The eight spatial-domain residual values.</param>
/// <param name="step">The eight-element stage buffer owned by the containing two-dimensional transform.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output[0] = input[0];
output[1] = input[4];
output[2] = input[2];
output[3] = input[6];
output[4] = input[1];
output[5] = input[5];
output[6] = input[3];
output[7] = input[7];
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
stage++;
step[0] = output[0];
step[1] = output[1];
step[2] = output[2];
step[3] = output[3];
step[4] = Av1Transform1dMath.HalfButterfly(cospi[56], output[4], -cospi[8], output[7], cosBit);
step[5] = Av1Transform1dMath.HalfButterfly(cospi[24], output[5], -cospi[40], output[6], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(cospi[40], output[5], cospi[24], output[6], cosBit);
step[7] = Av1Transform1dMath.HalfButterfly(cospi[8], output[4], cospi[56], output[7], cosBit);
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output[0] = Av1Transform1dMath.HalfButterfly(cospi[32], step[0], cospi[32], step[1], cosBit);
output[1] = Av1Transform1dMath.HalfButterfly(cospi[32], step[0], -cospi[32], step[1], cosBit);
output[2] = Av1Transform1dMath.HalfButterfly(cospi[48], step[2], -cospi[16], step[3], cosBit);
output[3] = Av1Transform1dMath.HalfButterfly(cospi[16], step[2], cospi[48], step[3], cosBit);
output[4] = Av1Transform1dMath.Clamp(step[4] + step[5], range);
output[5] = Av1Transform1dMath.Clamp(step[4] - step[5], range);
output[6] = Av1Transform1dMath.Clamp(step[7] - step[6], range);
output[7] = Av1Transform1dMath.Clamp(step[6] + step[7], range);
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
stage++;
step[0] = Av1Transform1dMath.Clamp(output[0] + output[3], range);
step[1] = Av1Transform1dMath.Clamp(output[1] + output[2], range);
step[2] = Av1Transform1dMath.Clamp(output[1] - output[2], range);
step[3] = Av1Transform1dMath.Clamp(output[0] - output[3], range);
step[4] = output[4];
step[5] = Av1Transform1dMath.HalfButterfly(-cospi[32], output[5], cospi[32], output[6], cosBit);
step[6] = Av1Transform1dMath.HalfButterfly(cospi[32], output[5], cospi[32], output[6], cosBit);
step[7] = output[7];
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output[0] = Av1Transform1dMath.Clamp(step[0] + step[7], range);
output[1] = Av1Transform1dMath.Clamp(step[1] + step[6], range);
output[2] = Av1Transform1dMath.Clamp(step[2] + step[5], range);
output[3] = Av1Transform1dMath.Clamp(step[3] + step[4], range);
output[4] = Av1Transform1dMath.Clamp(step[3] - step[4], range);
output[5] = Av1Transform1dMath.Clamp(step[2] - step[5], range);
output[6] = Av1Transform1dMath.Clamp(step[1] - step[6], range);
output[7] = Av1Transform1dMath.Clamp(step[0] - step[7], range);
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output.V0 = input.V0;
output.V1 = input.V4;
output.V2 = input.V2;
output.V3 = input.V6;
output.V4 = input.V1;
output.V5 = input.V5;
output.V6 = input.V3;
output.V7 = input.V7;
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V4, -cospi[8], output.V7, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V5, -cospi[40], output.V6, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V5, cospi[24], output.V6, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V4, cospi[56], output.V7, cosBit);
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, cospi[32], step.V1, cosBit);
output.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, -cospi[32], step.V1, cosBit);
output.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], step.V2, -cospi[16], step.V3, cosBit);
output.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], step.V2, cospi[48], step.V3, cosBit);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V5, range);
output.V5 = Av1Transform1dMath.Clamp(step.V4 - step.V5, range);
output.V6 = Av1Transform1dMath.Clamp(step.V7 - step.V6, range);
output.V7 = Av1Transform1dMath.Clamp(step.V6 + step.V7, range);
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
stage++;
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V3, range);
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V2, range);
step.V2 = Av1Transform1dMath.Clamp(output.V1 - output.V2, range);
step.V3 = Av1Transform1dMath.Clamp(output.V0 - output.V3, range);
step.V4 = output.V4;
step.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V5, cospi[32], output.V6, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V5, cospi[32], output.V6, cosBit);
step.V7 = output.V7;
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V7, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V6, range);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V5, range);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V4, range);
output.V4 = Av1Transform1dMath.Clamp(step.V3 - step.V4, range);
output.V5 = Av1Transform1dMath.Clamp(step.V2 - step.V5, range);
output.V6 = Av1Transform1dMath.Clamp(step.V1 - step.V6, range);
output.V7 = Av1Transform1dMath.Clamp(step.V0 - step.V7, range);
}
/// <summary>
/// Applies the transform to four independent axes in parallel.
/// </summary>
/// <param name="input">The source values for the parallel transform axes.</param>
/// <param name="output">The destination values for the parallel transform axes.</param>
/// <param name="step">The fixed stage storage for the parallel transform axes.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
/// <param name="stageRange">The signed-bit range assigned to each transform stage.</param>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
ReadOnlySpan<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
int stage = 0;
// Stage 1 permutes frequency-ordered coefficients into the recursive DCT factorization order.
stage++;
output.V0 = input.V0;
output.V1 = input.V4;
output.V2 = input.V2;
output.V3 = input.V6;
output.V4 = input.V1;
output.V5 = input.V5;
output.V6 = input.V3;
output.V7 = input.V7;
// Stage 2 rotates the odd-frequency coefficient pairs by their pi/16 angles.
stage++;
step.V0 = output.V0;
step.V1 = output.V1;
step.V2 = output.V2;
step.V3 = output.V3;
step.V4 = Av1Transform1dMath.HalfButterfly(cospi[56], output.V4, -cospi[8], output.V7, cosBit);
step.V5 = Av1Transform1dMath.HalfButterfly(cospi[24], output.V5, -cospi[40], output.V6, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[40], output.V5, cospi[24], output.V6, cosBit);
step.V7 = Av1Transform1dMath.HalfButterfly(cospi[8], output.V4, cospi[56], output.V7, cosBit);
// Stage 3 reconstructs the even four-point DCT and combines adjacent odd terms.
stage++;
byte range = stageRange[stage];
output.V0 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, cospi[32], step.V1, cosBit);
output.V1 = Av1Transform1dMath.HalfButterfly(cospi[32], step.V0, -cospi[32], step.V1, cosBit);
output.V2 = Av1Transform1dMath.HalfButterfly(cospi[48], step.V2, -cospi[16], step.V3, cosBit);
output.V3 = Av1Transform1dMath.HalfButterfly(cospi[16], step.V2, cospi[48], step.V3, cosBit);
output.V4 = Av1Transform1dMath.Clamp(step.V4 + step.V5, range);
output.V5 = Av1Transform1dMath.Clamp(step.V4 - step.V5, range);
output.V6 = Av1Transform1dMath.Clamp(step.V7 - step.V6, range);
output.V7 = Av1Transform1dMath.Clamp(step.V6 + step.V7, range);
// Stage 4 completes the even butterflies and applies the remaining pi/4 odd rotation.
stage++;
step.V0 = Av1Transform1dMath.Clamp(output.V0 + output.V3, range);
step.V1 = Av1Transform1dMath.Clamp(output.V1 + output.V2, range);
step.V2 = Av1Transform1dMath.Clamp(output.V1 - output.V2, range);
step.V3 = Av1Transform1dMath.Clamp(output.V0 - output.V3, range);
step.V4 = output.V4;
step.V5 = Av1Transform1dMath.HalfButterfly(-cospi[32], output.V5, cospi[32], output.V6, cosBit);
step.V6 = Av1Transform1dMath.HalfButterfly(cospi[32], output.V5, cospi[32], output.V6, cosBit);
step.V7 = output.V7;
// Stage 5 merges the even and odd halves into spatial order and clamps every result.
stage++;
range = stageRange[stage];
output.V0 = Av1Transform1dMath.Clamp(step.V0 + step.V7, range);
output.V1 = Av1Transform1dMath.Clamp(step.V1 + step.V6, range);
output.V2 = Av1Transform1dMath.Clamp(step.V2 + step.V5, range);
output.V3 = Av1Transform1dMath.Clamp(step.V3 + step.V4, range);
output.V4 = Av1Transform1dMath.Clamp(step.V3 - step.V4, range);
output.V5 = Av1Transform1dMath.Clamp(step.V2 - step.V5, range);
output.V6 = Av1Transform1dMath.Clamp(step.V1 - step.V6, range);
output.V7 = Av1Transform1dMath.Clamp(step.V0 - step.V7, range);
}
}

81
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dOperator.cs

@ -0,0 +1,81 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the sixteen-point AV1 inverse identity transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
/// </remarks>
internal readonly struct Av1Identity16Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative sixteen-point AV1 inverse identity transform.
/// </summary>
/// <param name="input">The sixteen frequency-domain coefficients.</param>
/// <param name="output">The sixteen scaled spatial-domain values.</param>
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
_ = step;
_ = cosBit;
_ = stageRange;
// The AV1 identity transform preserves coefficient order while applying the twice the square-root-of-two fixed-point scale required for 2-D normalization.
for (int i = 0; i < 16; i++)
{
output[i] = Av1Math.RoundShift((long)input[i] * (2 * Av1Transform1dMath.NewSqrt2), Av1Transform1dMath.NewSqrt2Bits);
}
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
// The doubled scale exceeds Int32 only for the 20-bit twelve-bit row range. Widen that exact product and
// rounding sequence, matching libaom without changing the established lower-range SIMD path.
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount)
{
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
else
{
Av1IdentityTransform1d.Transform(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
_ = step;
_ = cosBit;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount)
{
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
else
{
Av1IdentityTransform1d.Transform(ref input, ref output, 16, 2 * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
_ = step;
_ = cosBit;
}
}

65
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dOperator.cs

@ -0,0 +1,65 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the thirty-two-point AV1 inverse identity transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
/// </remarks>
internal readonly struct Av1Identity32Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative thirty-two-point AV1 inverse identity transform.
/// </summary>
/// <param name="input">The thirty-two frequency-domain coefficients.</param>
/// <param name="output">The thirty-two scaled spatial-domain values.</param>
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
_ = step;
_ = cosBit;
_ = stageRange;
// The AV1 identity transform preserves coefficient order while applying the exact factor-of-four scale required for 2-D normalization.
for (int i = 0; i < 32; i++)
{
output[i] = input[i] * 4;
}
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0);
_ = step;
_ = cosBit;
_ = stageRange;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 32, 4, 0);
_ = step;
_ = cosBit;
_ = stageRange;
}
}

81
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dOperator.cs

@ -0,0 +1,81 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the four-point AV1 inverse identity transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
/// </remarks>
internal readonly struct Av1Identity4Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative four-point AV1 inverse identity transform.
/// </summary>
/// <param name="input">The four frequency-domain coefficients.</param>
/// <param name="output">The four scaled spatial-domain values.</param>
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
_ = step;
_ = cosBit;
_ = stageRange;
// The AV1 identity transform preserves coefficient order while applying the square-root-of-two fixed-point scale required for 2-D normalization.
for (int i = 0; i < 4; i++)
{
output[i] = Av1Math.RoundShift((long)input[i] * Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
// Only a twelve-bit row transform has the 20-bit input range that can overflow this fixed-point product.
// Match libaom's high-bit-depth kernel there while retaining the compact Int32 path for narrower ranges.
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount)
{
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
else
{
Av1IdentityTransform1d.Transform(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
_ = step;
_ = cosBit;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
if (stageRange[0] >= Av1Transform1dMath.WidenedIntermediateBitCount)
{
Av1IdentityTransform1d.TransformWidened(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
else
{
Av1IdentityTransform1d.Transform(ref input, ref output, 4, Av1Transform1dMath.NewSqrt2, Av1Transform1dMath.NewSqrt2Bits);
}
_ = step;
_ = cosBit;
}
}

65
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dOperator.cs

@ -0,0 +1,65 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Defines the eight-point AV1 inverse identity transform operator.
/// </summary>
/// <remarks>
/// Vector fields represent transform positions and vector lanes represent independent axes. Scaling is lane-local,
/// so the SIMD overloads preserve the scalar fixed-point multiplier and rounding for every axis.
/// </remarks>
internal readonly struct Av1Identity8Inverse1dOperator : IAv1Transform1dOperator
{
/// <summary>
/// Applies the normative eight-point AV1 inverse identity transform.
/// </summary>
/// <param name="input">The eight frequency-domain coefficients.</param>
/// <param name="output">The eight scaled spatial-domain values.</param>
/// <param name="step">Unused stage storage supplied by the common transform-kernel contract.</param>
/// <param name="cosBit">Unused cosine precision supplied by the common transform-kernel contract.</param>
/// <param name="stageRange">The signed-bit range assigned to the transform output.</param>
public static void Transform(ReadOnlySpan<int> input, Span<int> output, Span<int> step, int cosBit, Av1TransformStageRange stageRange)
{
_ = step;
_ = cosBit;
_ = stageRange;
// The AV1 identity transform preserves coefficient order while applying the exact factor-of-two scale required for 2-D normalization.
for (int i = 0; i < 8; i++)
{
output[i] = input[i] * 2;
}
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector128<int>> input,
ref Av1TransformVector<Vector128<int>> output,
ref Av1TransformVector<Vector128<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0);
_ = step;
_ = cosBit;
_ = stageRange;
}
/// <inheritdoc/>
public static void Transform(
ref Av1TransformVector<Vector256<int>> input,
ref Av1TransformVector<Vector256<int>> output,
ref Av1TransformVector<Vector256<int>> step,
int cosBit,
Av1TransformStageRange stageRange)
{
Av1IdentityTransform1d.Transform(ref input, ref output, 8, 2, 0);
_ = step;
_ = cosBit;
_ = stageRange;
}
}

111
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ForwardTransformTests.cs

@ -5,6 +5,7 @@ using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
using SixLabors.ImageSharp.Tests.TestUtilities;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
@ -39,18 +40,18 @@ public class Av1ForwardTransformTests
[Fact]
public void OneDimensionalOperatorsMatchAnalyticalReference()
{
AssertOperatorAccuracy<Av1ForwardTransformer.Dct4Operator>(Av1TransformType1d.Dct, 4);
AssertOperatorAccuracy<Av1ForwardTransformer.Dct8Operator>(Av1TransformType1d.Dct, 8);
AssertOperatorAccuracy<Av1ForwardTransformer.Dct16Operator>(Av1TransformType1d.Dct, 16);
AssertOperatorAccuracy<Av1ForwardTransformer.Dct32Operator>(Av1TransformType1d.Dct, 32);
AssertOperatorAccuracy<Av1ForwardTransformer.Dct64Operator>(Av1TransformType1d.Dct, 64);
AssertOperatorAccuracy<Av1ForwardTransformer.Adst4Operator>(Av1TransformType1d.Adst, 4);
AssertOperatorAccuracy<Av1ForwardTransformer.Adst8Operator>(Av1TransformType1d.Adst, 8);
AssertOperatorAccuracy<Av1ForwardTransformer.Adst16Operator>(Av1TransformType1d.Adst, 16);
AssertOperatorAccuracy<Av1ForwardTransformer.Identity4Operator>(Av1TransformType1d.Identity, 4);
AssertOperatorAccuracy<Av1ForwardTransformer.Identity8Operator>(Av1TransformType1d.Identity, 8);
AssertOperatorAccuracy<Av1ForwardTransformer.Identity16Operator>(Av1TransformType1d.Identity, 16);
AssertOperatorAccuracy<Av1ForwardTransformer.Identity32Operator>(Av1TransformType1d.Identity, 32);
AssertOperatorAccuracy<Av1Dct4Forward1dOperator>(Av1TransformType1d.Dct, 4);
AssertOperatorAccuracy<Av1Dct8Forward1dOperator>(Av1TransformType1d.Dct, 8);
AssertOperatorAccuracy<Av1Dct16Forward1dOperator>(Av1TransformType1d.Dct, 16);
AssertOperatorAccuracy<Av1Dct32Forward1dOperator>(Av1TransformType1d.Dct, 32);
AssertOperatorAccuracy<Av1Dct64Forward1dOperator>(Av1TransformType1d.Dct, 64);
AssertOperatorAccuracy<Av1Adst4Forward1dOperator>(Av1TransformType1d.Adst, 4);
AssertOperatorAccuracy<Av1Adst8Forward1dOperator>(Av1TransformType1d.Adst, 8);
AssertOperatorAccuracy<Av1Adst16Forward1dOperator>(Av1TransformType1d.Adst, 16);
AssertOperatorAccuracy<Av1Identity4Forward1dOperator>(Av1TransformType1d.Identity, 4);
AssertOperatorAccuracy<Av1Identity8Forward1dOperator>(Av1TransformType1d.Identity, 8);
AssertOperatorAccuracy<Av1Identity16Forward1dOperator>(Av1TransformType1d.Identity, 16);
AssertOperatorAccuracy<Av1Identity32Forward1dOperator>(Av1TransformType1d.Identity, 32);
}
/// <summary>
@ -87,18 +88,18 @@ public class Av1ForwardTransformTests
/// </summary>
private static void AssertOneDimensionalOperators()
{
AssertOperator<Av1ForwardTransformer.Dct4Operator>(4);
AssertOperator<Av1ForwardTransformer.Dct8Operator>(8);
AssertOperator<Av1ForwardTransformer.Dct16Operator>(16);
AssertOperator<Av1ForwardTransformer.Dct32Operator>(32);
AssertOperator<Av1ForwardTransformer.Dct64Operator>(64);
AssertOperator<Av1ForwardTransformer.Adst4Operator>(4);
AssertOperator<Av1ForwardTransformer.Adst8Operator>(8);
AssertOperator<Av1ForwardTransformer.Adst16Operator>(16);
AssertOperator<Av1ForwardTransformer.Identity4Operator>(4);
AssertOperator<Av1ForwardTransformer.Identity8Operator>(8);
AssertOperator<Av1ForwardTransformer.Identity16Operator>(16);
AssertOperator<Av1ForwardTransformer.Identity32Operator>(32);
AssertOperator<Av1Dct4Forward1dOperator>(4);
AssertOperator<Av1Dct8Forward1dOperator>(8);
AssertOperator<Av1Dct16Forward1dOperator>(16);
AssertOperator<Av1Dct32Forward1dOperator>(32);
AssertOperator<Av1Dct64Forward1dOperator>(64);
AssertOperator<Av1Adst4Forward1dOperator>(4);
AssertOperator<Av1Adst8Forward1dOperator>(8);
AssertOperator<Av1Adst16Forward1dOperator>(16);
AssertOperator<Av1Identity4Forward1dOperator>(4);
AssertOperator<Av1Identity8Forward1dOperator>(8);
AssertOperator<Av1Identity16Forward1dOperator>(16);
AssertOperator<Av1Identity32Forward1dOperator>(32);
}
/// <summary>
@ -107,7 +108,7 @@ public class Av1ForwardTransformTests
/// <typeparam name="TOperator">The transform operator.</typeparam>
/// <param name="length">The transform length.</param>
private static void AssertOperator<TOperator>(int length)
where TOperator : struct, Av1ForwardTransformer.IAv1ForwardTransform1dOperator
where TOperator : struct, IAv1ForwardTransform1dOperator
{
const int cosBit = 12;
@ -143,7 +144,7 @@ public class Av1ForwardTransformTests
/// <param name="transformType">The analytical transform definition.</param>
/// <param name="length">The transform length.</param>
private static void AssertOperatorAccuracy<TOperator>(Av1TransformType1d transformType, int length)
where TOperator : struct, Av1ForwardTransformer.IAv1ForwardTransform1dOperator
where TOperator : struct, IAv1ForwardTransform1dOperator
{
const int cosBit = 13;
const int testBlockCount = 500;
@ -190,7 +191,7 @@ public class Av1ForwardTransformTests
/// <param name="length">The transform length.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
private static void AssertInt32Operator<TOperator, TVector>(int length, int cosBit)
where TOperator : struct, Av1ForwardTransformer.IAv1ForwardTransform1dOperator
where TOperator : struct, IAv1ForwardTransform1dOperator
where TVector : struct
{
int laneCount = System.Runtime.CompilerServices.Unsafe.SizeOf<TVector>() / sizeof(int);
@ -244,7 +245,7 @@ public class Av1ForwardTransformTests
/// <param name="length">The transform length.</param>
/// <param name="cosBit">The fixed-point precision of the cosine constants.</param>
private static void AssertInt16Operator<TOperator, TVector>(int length, int cosBit)
where TOperator : struct, Av1ForwardTransformer.IAv1ForwardTransform1dOperator
where TOperator : struct, IAv1ForwardTransform1dOperator
where TVector : struct
{
int laneCount = System.Runtime.CompilerServices.Unsafe.SizeOf<TVector>() / sizeof(short);
@ -367,40 +368,40 @@ public class Av1ForwardTransformTests
switch (config.TransformFunctionTypeColumn)
{
case Av1TransformFunctionType.Dct4:
DispatchReferenceRow<Av1ForwardTransformer.Dct4Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Dct4Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Dct8:
DispatchReferenceRow<Av1ForwardTransformer.Dct8Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Dct8Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Dct16:
DispatchReferenceRow<Av1ForwardTransformer.Dct16Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Dct16Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Dct32:
DispatchReferenceRow<Av1ForwardTransformer.Dct32Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Dct32Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Dct64:
DispatchReferenceRow<Av1ForwardTransformer.Dct64Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Dct64Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Adst4:
DispatchReferenceRow<Av1ForwardTransformer.Adst4Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Adst4Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Adst8:
DispatchReferenceRow<Av1ForwardTransformer.Adst8Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Adst8Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Adst16:
DispatchReferenceRow<Av1ForwardTransformer.Adst16Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Adst16Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Identity4:
DispatchReferenceRow<Av1ForwardTransformer.Identity4Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Identity4Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Identity8:
DispatchReferenceRow<Av1ForwardTransformer.Identity8Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Identity8Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Identity16:
DispatchReferenceRow<Av1ForwardTransformer.Identity16Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Identity16Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Identity32:
DispatchReferenceRow<Av1ForwardTransformer.Identity32Operator>(input, stride, output, ref config);
DispatchReferenceRow<Av1Identity32Forward1dOperator>(input, stride, output, ref config);
break;
}
}
@ -414,45 +415,45 @@ public class Av1ForwardTransformTests
/// <param name="output">The destination reference coefficients.</param>
/// <param name="config">The resolved transform functions, shifts, and axis orientation.</param>
private static void DispatchReferenceRow<TColumnOperator>(Span<short> input, int stride, Span<int> output, ref Av1Transform2dFlipConfiguration config)
where TColumnOperator : struct, Av1ForwardTransformer.IAv1ForwardTransform1dOperator
where TColumnOperator : struct, IAv1ForwardTransform1dOperator
{
switch (config.TransformFunctionTypeRow)
{
case Av1TransformFunctionType.Dct4:
TransformReference<TColumnOperator, Av1ForwardTransformer.Dct4Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Dct4Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Dct8:
TransformReference<TColumnOperator, Av1ForwardTransformer.Dct8Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Dct8Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Dct16:
TransformReference<TColumnOperator, Av1ForwardTransformer.Dct16Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Dct16Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Dct32:
TransformReference<TColumnOperator, Av1ForwardTransformer.Dct32Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Dct32Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Dct64:
TransformReference<TColumnOperator, Av1ForwardTransformer.Dct64Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Dct64Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Adst4:
TransformReference<TColumnOperator, Av1ForwardTransformer.Adst4Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Adst4Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Adst8:
TransformReference<TColumnOperator, Av1ForwardTransformer.Adst8Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Adst8Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Adst16:
TransformReference<TColumnOperator, Av1ForwardTransformer.Adst16Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Adst16Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Identity4:
TransformReference<TColumnOperator, Av1ForwardTransformer.Identity4Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Identity4Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Identity8:
TransformReference<TColumnOperator, Av1ForwardTransformer.Identity8Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Identity8Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Identity16:
TransformReference<TColumnOperator, Av1ForwardTransformer.Identity16Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Identity16Forward1dOperator>(input, stride, output, ref config);
break;
case Av1TransformFunctionType.Identity32:
TransformReference<TColumnOperator, Av1ForwardTransformer.Identity32Operator>(input, stride, output, ref config);
TransformReference<TColumnOperator, Av1Identity32Forward1dOperator>(input, stride, output, ref config);
break;
}
}
@ -471,8 +472,8 @@ public class Av1ForwardTransformTests
int stride,
Span<int> output,
ref Av1Transform2dFlipConfiguration config)
where TColumnOperator : struct, Av1ForwardTransformer.IAv1ForwardTransform1dOperator
where TRowOperator : struct, Av1ForwardTransformer.IAv1ForwardTransform1dOperator
where TColumnOperator : struct, IAv1ForwardTransform1dOperator
where TRowOperator : struct, IAv1ForwardTransform1dOperator
{
int width = config.TransformSize.GetWidth();
int height = config.TransformSize.GetHeight();

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

@ -4,6 +4,8 @@
using System.Runtime.Intrinsics;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
using SixLabors.ImageSharp.Tests.TestUtilities;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
@ -53,11 +55,11 @@ public class Av1InverseTransformTests
/// </summary>
private static void AssertDctOperatorParity()
{
AssertOperatorParity<Av1InverseTransformer.Dct4Operator>(4);
AssertOperatorParity<Av1InverseTransformer.Dct8Operator>(8);
AssertOperatorParity<Av1InverseTransformer.Dct16Operator>(16);
AssertOperatorParity<Av1InverseTransformer.Dct32Operator>(32);
AssertOperatorParity<Av1InverseTransformer.Dct64Operator>(64);
AssertOperatorParity<Av1Dct4Inverse1dOperator>(4);
AssertOperatorParity<Av1Dct8Inverse1dOperator>(8);
AssertOperatorParity<Av1Dct16Inverse1dOperator>(16);
AssertOperatorParity<Av1Dct32Inverse1dOperator>(32);
AssertOperatorParity<Av1Dct64Inverse1dOperator>(64);
}
/// <summary>
@ -65,9 +67,9 @@ public class Av1InverseTransformTests
/// </summary>
private static void AssertAdstOperatorParity()
{
AssertOperatorParity<Av1InverseTransformer.Adst4Operator>(4);
AssertOperatorParity<Av1InverseTransformer.Adst8Operator>(8);
AssertOperatorParity<Av1InverseTransformer.Adst16Operator>(16);
AssertOperatorParity<Av1Adst4Inverse1dOperator>(4);
AssertOperatorParity<Av1Adst8Inverse1dOperator>(8);
AssertOperatorParity<Av1Adst16Inverse1dOperator>(16);
}
/// <summary>
@ -75,10 +77,10 @@ public class Av1InverseTransformTests
/// </summary>
private static void AssertIdentityOperatorParity()
{
AssertOperatorParity<Av1InverseTransformer.Identity4Operator>(4);
AssertOperatorParity<Av1InverseTransformer.Identity8Operator>(8);
AssertOperatorParity<Av1InverseTransformer.Identity16Operator>(16);
AssertOperatorParity<Av1InverseTransformer.Identity32Operator>(32);
AssertOperatorParity<Av1Identity4Inverse1dOperator>(4);
AssertOperatorParity<Av1Identity8Inverse1dOperator>(8);
AssertOperatorParity<Av1Identity16Inverse1dOperator>(16);
AssertOperatorParity<Av1Identity32Inverse1dOperator>(32);
}
/// <summary>
@ -108,14 +110,14 @@ public class Av1InverseTransformTests
Av1TransformVector<Vector256<int>> adstOutput256 = default;
Av1TransformVector<Vector256<int>> adstStep256 = default;
Av1InverseTransformer.Adst4Operator.Transform(
Av1Adst4Inverse1dOperator.Transform(
ref adstInput128,
ref adstOutput128,
ref adstStep128,
cosBit,
stageRange);
Av1InverseTransformer.Adst4Operator.Transform(
Av1Adst4Inverse1dOperator.Transform(
ref adstInput256,
ref adstOutput256,
ref adstStep256,
@ -148,7 +150,7 @@ public class Av1InverseTransformTests
262_143,
-262_144);
AssertWidenedIdentityOperator<Av1InverseTransformer.Identity4Operator>(
AssertWidenedIdentityOperator<Av1Identity4Inverse1dOperator>(
4,
identityInput128,
Vector128.Create(741_503, -741_504, 741_501, -741_503),
@ -156,7 +158,7 @@ public class Av1InverseTransformTests
Vector256.Create(741_503, -741_504, 741_501, -741_503, 524_322, -524_323, 370_751, -370_752),
stageRange);
AssertWidenedIdentityOperator<Av1InverseTransformer.Identity16Operator>(
AssertWidenedIdentityOperator<Av1Identity16Inverse1dOperator>(
16,
identityInput128,
Vector128.Create(1_483_005, -1_483_008, 1_483_002, -1_483_005),
@ -182,7 +184,7 @@ public class Av1InverseTransformTests
Vector256<int> input256,
Vector256<int> expected256,
Av1TransformStageRange stageRange)
where TOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TOperator : struct, IAv1Transform1dOperator
{
const int cosBit = 12;
Av1TransformVector<Vector128<int>> values128 = default;
@ -268,18 +270,18 @@ public class Av1InverseTransformTests
[Fact]
public void ForwardAndInverseOperatorPairsReconstructTheirInput()
{
AssertRoundTrip<Av1ForwardTransformer.Dct4Operator, Av1InverseTransformer.Dct4Operator>(Av1TransformType.DctDct, Av1TransformSize.Size4x4, 1, 1);
AssertRoundTrip<Av1ForwardTransformer.Dct8Operator, Av1InverseTransformer.Dct8Operator>(Av1TransformType.DctDct, Av1TransformSize.Size8x8, 2, 2);
AssertRoundTrip<Av1ForwardTransformer.Dct16Operator, Av1InverseTransformer.Dct16Operator>(Av1TransformType.DctDct, Av1TransformSize.Size16x16, 3, 3);
AssertRoundTrip<Av1ForwardTransformer.Dct32Operator, Av1InverseTransformer.Dct32Operator>(Av1TransformType.DctDct, Av1TransformSize.Size32x32, 4, 4);
AssertRoundTrip<Av1ForwardTransformer.Dct64Operator, Av1InverseTransformer.Dct64Operator>(Av1TransformType.DctDct, Av1TransformSize.Size64x64, 5, 5);
AssertRoundTrip<Av1ForwardTransformer.Adst4Operator, Av1InverseTransformer.Adst4Operator>(Av1TransformType.AdstAdst, Av1TransformSize.Size4x4, 1, 1);
AssertRoundTrip<Av1ForwardTransformer.Adst8Operator, Av1InverseTransformer.Adst8Operator>(Av1TransformType.AdstAdst, Av1TransformSize.Size8x8, 2, 2);
AssertRoundTrip<Av1ForwardTransformer.Adst16Operator, Av1InverseTransformer.Adst16Operator>(Av1TransformType.AdstAdst, Av1TransformSize.Size16x16, 3, 3);
AssertRoundTrip<Av1ForwardTransformer.Identity4Operator, Av1InverseTransformer.Identity4Operator>(Av1TransformType.Identity, Av1TransformSize.Size4x4, 1, 1);
AssertRoundTrip<Av1ForwardTransformer.Identity8Operator, Av1InverseTransformer.Identity8Operator>(Av1TransformType.Identity, Av1TransformSize.Size8x8, 2, 1);
AssertRoundTrip<Av1ForwardTransformer.Identity16Operator, Av1InverseTransformer.Identity16Operator>(Av1TransformType.Identity, Av1TransformSize.Size16x16, 3, 1);
AssertRoundTrip<Av1ForwardTransformer.Identity32Operator, Av1InverseTransformer.Identity32Operator>(Av1TransformType.Identity, Av1TransformSize.Size32x32, 4, 1);
AssertRoundTrip<Av1Dct4Forward1dOperator, Av1Dct4Inverse1dOperator>(Av1TransformType.DctDct, Av1TransformSize.Size4x4, 1, 1);
AssertRoundTrip<Av1Dct8Forward1dOperator, Av1Dct8Inverse1dOperator>(Av1TransformType.DctDct, Av1TransformSize.Size8x8, 2, 2);
AssertRoundTrip<Av1Dct16Forward1dOperator, Av1Dct16Inverse1dOperator>(Av1TransformType.DctDct, Av1TransformSize.Size16x16, 3, 3);
AssertRoundTrip<Av1Dct32Forward1dOperator, Av1Dct32Inverse1dOperator>(Av1TransformType.DctDct, Av1TransformSize.Size32x32, 4, 4);
AssertRoundTrip<Av1Dct64Forward1dOperator, Av1Dct64Inverse1dOperator>(Av1TransformType.DctDct, Av1TransformSize.Size64x64, 5, 5);
AssertRoundTrip<Av1Adst4Forward1dOperator, Av1Adst4Inverse1dOperator>(Av1TransformType.AdstAdst, Av1TransformSize.Size4x4, 1, 1);
AssertRoundTrip<Av1Adst8Forward1dOperator, Av1Adst8Inverse1dOperator>(Av1TransformType.AdstAdst, Av1TransformSize.Size8x8, 2, 2);
AssertRoundTrip<Av1Adst16Forward1dOperator, Av1Adst16Inverse1dOperator>(Av1TransformType.AdstAdst, Av1TransformSize.Size16x16, 3, 3);
AssertRoundTrip<Av1Identity4Forward1dOperator, Av1Identity4Inverse1dOperator>(Av1TransformType.Identity, Av1TransformSize.Size4x4, 1, 1);
AssertRoundTrip<Av1Identity8Forward1dOperator, Av1Identity8Inverse1dOperator>(Av1TransformType.Identity, Av1TransformSize.Size8x8, 2, 1);
AssertRoundTrip<Av1Identity16Forward1dOperator, Av1Identity16Inverse1dOperator>(Av1TransformType.Identity, Av1TransformSize.Size16x16, 3, 1);
AssertRoundTrip<Av1Identity32Forward1dOperator, Av1Identity32Inverse1dOperator>(Av1TransformType.Identity, Av1TransformSize.Size32x32, 4, 1);
}
/// <summary>
@ -596,7 +598,7 @@ public class Av1InverseTransformTests
/// <typeparam name="TOperator">The inverse transform operator.</typeparam>
/// <param name="length">The transform length.</param>
private static void AssertOperatorParity<TOperator>(int length)
where TOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TOperator : struct, IAv1Transform1dOperator
{
const int cosBit = 12;
Av1TransformStageRange stageRange = default;
@ -670,8 +672,8 @@ public class Av1InverseTransformTests
/// <param name="scaleLog2">The power-of-two scale applied by the operator pair.</param>
/// <param name="allowedError">The maximum permitted reconstruction error.</param>
private static void AssertRoundTrip<TForwardOperator, TInverseOperator>(Av1TransformType transformType, Av1TransformSize transformSize, int scaleLog2, int allowedError)
where TForwardOperator : struct, Av1ForwardTransformer.IAv1ForwardTransform1dOperator
where TInverseOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TForwardOperator : struct, IAv1ForwardTransform1dOperator
where TInverseOperator : struct, IAv1Transform1dOperator
{
const int bitDepth = 10;
const int testBlockCount = 30;
@ -730,40 +732,40 @@ public class Av1InverseTransformTests
switch (config.TransformFunctionTypeColumn)
{
case Av1TransformFunctionType.Dct4:
DispatchRow<Av1InverseTransformer.Dct4Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Dct4Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Dct8:
DispatchRow<Av1InverseTransformer.Dct8Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Dct8Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Dct16:
DispatchRow<Av1InverseTransformer.Dct16Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Dct16Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Dct32:
DispatchRow<Av1InverseTransformer.Dct32Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Dct32Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Dct64:
DispatchRow<Av1InverseTransformer.Dct64Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Dct64Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Adst4:
DispatchRow<Av1InverseTransformer.Adst4Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Adst4Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Adst8:
DispatchRow<Av1InverseTransformer.Adst8Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Adst8Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Adst16:
DispatchRow<Av1InverseTransformer.Adst16Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Adst16Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Identity4:
DispatchRow<Av1InverseTransformer.Identity4Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Identity4Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Identity8:
DispatchRow<Av1InverseTransformer.Identity8Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Identity8Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Identity16:
DispatchRow<Av1InverseTransformer.Identity16Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Identity16Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Identity32:
DispatchRow<Av1InverseTransformer.Identity32Operator>(transformType, transformSize, bitDepth, ref config);
DispatchRow<Av1Identity32Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
default:
Assert.Fail($"Unexpected column function {config.TransformFunctionTypeColumn} for {transformType} {transformSize}.");
@ -784,45 +786,45 @@ public class Av1InverseTransformTests
Av1TransformSize transformSize,
int bitDepth,
ref Av1Transform2dFlipConfiguration config)
where TColumnOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TColumnOperator : struct, IAv1Transform1dOperator
{
switch (config.TransformFunctionTypeRow)
{
case Av1TransformFunctionType.Dct4:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Dct4Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Dct4Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Dct8:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Dct8Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Dct8Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Dct16:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Dct16Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Dct16Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Dct32:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Dct32Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Dct32Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Dct64:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Dct64Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Dct64Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Adst4:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Adst4Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Adst4Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Adst8:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Adst8Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Adst8Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Adst16:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Adst16Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Adst16Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Identity4:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Identity4Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Identity4Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Identity8:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Identity8Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Identity8Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Identity16:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Identity16Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Identity16Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
case Av1TransformFunctionType.Identity32:
AssertTransform2dParity<TColumnOperator, Av1InverseTransformer.Identity32Operator>(transformType, transformSize, bitDepth, ref config);
AssertTransform2dParity<TColumnOperator, Av1Identity32Inverse1dOperator>(transformType, transformSize, bitDepth, ref config);
break;
default:
Assert.Fail($"Unexpected row function {config.TransformFunctionTypeRow} for {transformType} {transformSize}.");
@ -844,8 +846,8 @@ public class Av1InverseTransformTests
Av1TransformSize transformSize,
int bitDepth,
ref Av1Transform2dFlipConfiguration config)
where TColumnOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TRowOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TColumnOperator : struct, IAv1Transform1dOperator
where TRowOperator : struct, IAv1Transform1dOperator
{
int width = transformSize.GetWidth();
int height = transformSize.GetHeight();
@ -895,8 +897,8 @@ public class Av1InverseTransformTests
int[] coefficients,
Av1TransformSize transformSize,
ref Av1Transform2dFlipConfiguration config)
where TColumnOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TRowOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TColumnOperator : struct, IAv1Transform1dOperator
where TRowOperator : struct, IAv1Transform1dOperator
{
const int bitDepth = 8;
int width = transformSize.GetWidth();
@ -921,10 +923,10 @@ public class Av1InverseTransformTests
Array.Fill(scalar, byte.MaxValue);
Array.Fill(vector128, byte.MaxValue);
Av1InverseTransformer.Transform2dScalar<byte, Av1InverseTransformer.OutputOperator<byte>, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dScalar<byte, Av1InverseTransformOutputOperator<byte>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, scalar, writeStride, ref config, scalarWorkspace, bitDepth);
Av1InverseTransformer.Transform2dVector128<byte, Av1InverseTransformer.OutputOperator<byte>, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dVector128<byte, Av1InverseTransformOutputOperator<byte>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, vector128, writeStride, ref config, vector128Workspace, bitDepth);
Assert.Equal(scalar, vector128);
@ -935,7 +937,7 @@ public class Av1InverseTransformTests
int[] vector256Workspace = new int[workspaceLength];
Array.Fill(vector256, byte.MaxValue);
Av1InverseTransformer.Transform2dVector256<byte, Av1InverseTransformer.OutputOperator<byte>, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dVector256<byte, Av1InverseTransformOutputOperator<byte>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, vector256, writeStride, ref config, vector256Workspace, bitDepth);
Assert.Equal(scalar, vector256);
@ -956,8 +958,8 @@ public class Av1InverseTransformTests
Av1TransformSize transformSize,
int bitDepth,
ref Av1Transform2dFlipConfiguration config)
where TColumnOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TRowOperator : struct, Av1InverseTransformer.IAv1InverseTransform1dOperator
where TColumnOperator : struct, IAv1Transform1dOperator
where TRowOperator : struct, IAv1Transform1dOperator
{
int width = transformSize.GetWidth();
int height = transformSize.GetHeight();
@ -982,10 +984,10 @@ public class Av1InverseTransformTests
Array.Fill(scalar, short.MinValue);
Array.Fill(vector128, short.MinValue);
Av1InverseTransformer.Transform2dScalar<short, Av1InverseTransformer.OutputOperator<short>, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dScalar<short, Av1InverseTransformOutputOperator<short>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, scalar, writeStride, ref config, scalarWorkspace, bitDepth);
Av1InverseTransformer.Transform2dVector128<short, Av1InverseTransformer.OutputOperator<short>, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dVector128<short, Av1InverseTransformOutputOperator<short>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, vector128, writeStride, ref config, vector128Workspace, bitDepth);
Assert.Equal(scalar, vector128);
@ -996,7 +998,7 @@ public class Av1InverseTransformTests
int[] vector256Workspace = new int[workspaceLength];
Array.Fill(vector256, short.MinValue);
Av1InverseTransformer.Transform2dVector256<short, Av1InverseTransformer.OutputOperator<short>, TColumnOperator, TRowOperator>(
Av1Inverse2dTransformer.Transform2dVector256<short, Av1InverseTransformOutputOperator<short>, TColumnOperator, TRowOperator>(
coefficients, prediction, readStride, vector256, writeStride, ref config, vector256Workspace, bitDepth);
Assert.Equal(scalar, vector256);

Loading…
Cancel
Save