Browse Source

Document HEIF AV1 transform pipeline

pull/2633/head
James Jackson-South 1 week ago
parent
commit
f31f33d06d
  1. 92
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1BlockDecoder.cs
  2. 3
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1CoefficientShape.cs
  3. 80
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs
  4. 64
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformerFactory.cs
  5. 51
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1Inverse2dTransformer.cs
  6. 108
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformMath.cs
  7. 44
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.cs
  8. 30
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformerFactory.cs
  9. 33
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrder.cs
  10. 19
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrderConstants.cs
  11. 45
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1SinusConstants.cs
  12. 145
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs
  13. 3
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformClass.cs
  14. 21
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformFunctionParameters.cs
  15. 3
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformFunctionType.cs
  16. 3
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformMode.cs
  17. 3
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSetType.cs
  18. 3
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs
  19. 115
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs
  20. 3
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformType.cs
  21. 3
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformType1d.cs
  22. 20
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformTypeExtensions.cs
  23. 10
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst16Forward1dTransformer.cs
  24. 10
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst32Forward1dTransformer.cs
  25. 10
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst4Forward1dTransformer.cs
  26. 10
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst8Forward1dTransformer.cs
  27. 10
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct16Forward1dTransformer.cs
  28. 10
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct32Forward1dTransformer.cs
  29. 19
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct4Forward1dTransformer.cs
  30. 10
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct64Forward1dTransformer.cs
  31. 10
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct8Forward1dTransformer.cs
  32. 28
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1DctDct4Forward2dTransformer.cs
  33. 35
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Forward2dTransformerBase.cs
  34. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity16Forward1dTransformer.cs
  35. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity32Forward1dTransformer.cs
  36. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity4Forward1dTransformer.cs
  37. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity64Forward1dTransformer.cs
  38. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity8Forward1dTransformer.cs
  39. 2
      src/ImageSharp/Formats/Heif/Av1/Transform/IAv1Transformer1d.cs
  40. 11
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst16Inverse1dTransformer.cs
  41. 11
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst32Inverse1dTransformer.cs
  42. 11
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dTransformer.cs
  43. 11
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst8Inverse1dTransformer.cs
  44. 26
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct16Inverse1dTransformer.cs
  45. 26
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct32Inverse1dTransformer.cs
  46. 46
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dTransformer.cs
  47. 11
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct64Inverse1dTransformer.cs
  48. 26
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct8Inverse1dTransformer.cs
  49. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dTransformer.cs
  50. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dTransformer.cs
  51. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dTransformer.cs
  52. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity64Inverse1dTransformer.cs
  53. 9
      src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dTransformer.cs

92
src/ImageSharp/Formats/Heif/Av1/Transform/Av1BlockDecoder.cs

@ -10,47 +10,93 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Reconstructs AV1 transform blocks by combining prediction, inverse quantization, and inverse transforms.
/// </summary>
internal class Av1BlockDecoder
{
/// <summary>
/// The sequence-level syntax that determines superblock size, plane layout, and sample depth.
/// </summary>
private readonly ObuSequenceHeader sequenceHeader;
/// <summary>
/// The current frame syntax that determines quantization, lossless segments, and reconstruction geometry.
/// </summary>
private readonly ObuFrameHeader frameHeader;
/// <summary>
/// The reconstructed Y, U, and V sample planes receiving prediction and residual output.
/// </summary>
private readonly Av1FrameBuffer<byte> frameBuffer;
/// <summary>
/// Indicates whether transform traversal must also populate loop-filter parameters.
/// </summary>
private readonly bool isLoopFilterEnabled;
/// <summary>
/// The next packed coefficient position for each plane in the current superblock.
/// </summary>
private readonly int[] currentCoefficientIndex;
/// <summary>
/// Accumulates reconstructed luma samples until a chroma-from-luma prediction block can consume them.
/// </summary>
private readonly Av1ChromaFromLumaContext chromaFromLumaContext;
/// <summary>
/// Initializes a new instance of the <see cref="Av1BlockDecoder"/> class.
/// </summary>
/// <param name="sequenceHeader">The decoded sequence header.</param>
/// <param name="frameHeader">The decoded frame header.</param>
/// <param name="frameBuffer">The frame buffer receiving reconstructed samples.</param>
public Av1BlockDecoder(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader, Av1FrameBuffer<byte> frameBuffer)
{
this.sequenceHeader = sequenceHeader;
this.frameHeader = frameHeader;
this.frameBuffer = frameBuffer;
int ySize = (1 << this.sequenceHeader.SuperblockSizeLog2) * (1 << this.sequenceHeader.SuperblockSizeLog2);
// One scratch plane is reused for every transform unit. Its maximum size must cover a complete superblock
// across all coded planes, with chroma dimensions reduced independently by their subsampling axes.
int inverseQuantizationSize = ySize +
(this.sequenceHeader.ColorConfig.SubSamplingX ? ySize >> 2 : ySize) +
(this.sequenceHeader.ColorConfig.SubSamplingY ? ySize >> 2 : ySize);
this.CurrentInverseQuantizationCoefficients = new int[inverseQuantizationSize];
this.isLoopFilterEnabled = false;
this.currentCoefficientIndex = new int[3];
this.chromaFromLumaContext = new(sequenceHeader.ColorConfig);
}
/// <summary>
/// Gets the reusable raster-order coefficient buffer populated by inverse quantization.
/// </summary>
public int[] CurrentInverseQuantizationCoefficients { get; private set; }
/// <summary>
/// Resets the per-plane packed coefficient cursors before reconstructing a superblock.
/// </summary>
/// <param name="superblockInfo">The superblock whose coefficient streams will be consumed.</param>
public void UpdateSuperblock(Av1SuperblockInfo superblockInfo)
{
// Each superblock owns independent packed coefficient streams for Y, U, and V. The first value for each
// transform unit stores its coefficient count, so DecodeBlock advances a plane cursor as units are consumed.
this.currentCoefficientIndex[0] = 0;
this.currentCoefficientIndex[1] = 0;
this.currentCoefficientIndex[2] = 0;
}
/// <summary>
/// SVT: svt_aom_decode_block
/// Reconstructs every luma and chroma transform unit belonging to one decoded AV1 block.
/// </summary>
/// <param name="modeInfo">The decoded prediction, segmentation, skip, and transform state.</param>
/// <param name="modeInfoPosition">The block origin in units of four luma samples.</param>
/// <param name="blockSize">The decoded block size.</param>
/// <param name="superblockInfo">The owning superblock's transform and coefficient storage.</param>
/// <param name="tileInfo">The tile boundaries used to determine neighbor availability.</param>
/// <remarks>Corresponds to <c>svt_aom_decode_block</c> in the original WIP reference.</remarks>
public void DecodeBlock(Av1BlockModeInfo modeInfo, Point modeInfoPosition, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo)
{
ObuColorConfig colorConfig = this.sequenceHeader.ColorConfig;
@ -69,6 +115,8 @@ internal class Av1BlockDecoder
if (hasChroma)
{
// A one-unit luma edge maps to the same chroma sample as the adjacent unit on a subsampled axis. In that
// case the usable chroma neighbor is two mode-info units away rather than immediately above or left.
if (colorConfig.SubSamplingY && blockSize.Get4x4HighCount() == 1)
{
partitionInfo.AvailableAboveForChroma = modeInfoPosition.Y - 2 >= tileInfo.ModeInfoRowStart;
@ -88,8 +136,9 @@ internal class Av1BlockDecoder
bool isLossless = this.frameHeader.LosslessArray[modeInfo.SegmentId];
bool isLosslessBlock = isLossless && ((blockSize >= Av1BlockSize.Block64x64) && (blockSize <= Av1BlockSize.Block128x128));
int chromaTransformUnitCount = isLosslessBlock
? (maxBlocksWide * maxBlocksHigh) >> ((colorConfig.SubSamplingX ? 1 : 0) + (colorConfig.SubSamplingY ? 1 : 0))
: modeInfo.TransformUnitsCount[(int)Av1Plane.U];
? (maxBlocksWide * maxBlocksHigh) >> ((colorConfig.SubSamplingX ? 1 : 0) + (colorConfig.SubSamplingY ? 1 : 0))
: modeInfo.TransformUnitsCount[(int)Av1Plane.U];
bool highBitDepth = this.frameBuffer.BytesPerSample == 2;
int loopFilterStride = this.frameHeader.ModeInfoStride;
Av1PredictionDecoder predictionDecoder = new(this.sequenceHeader, this.frameHeader);
@ -105,6 +154,8 @@ internal class Av1BlockDecoder
continue;
}
// Luma transform descriptors occupy their own stream. U and V share one stream, with the V descriptors
// following the U descriptors for this block, so the V base includes the complete U transform-unit count.
int transformInfoIndex = plane switch
{
2 => superblockInfo.TransformInfoIndexUv + modeInfo.FirstTransformLocation[plane - 1] + chromaTransformUnitCount,
@ -130,9 +181,13 @@ internal class Av1BlockDecoder
Point pixelPosition = new(
(modeInfoPosition.X >> subX) << Av1Constants.ModeInfoSizeLog2,
(modeInfoPosition.Y >> subY) << Av1Constants.ModeInfoSizeLog2);
Span<byte> blockReconstructionBuffer = default;
Span<short> highBitDepthBlockReconstructionBuffer = default;
int reconstructionStride;
// Prediction reads the row immediately above the destination through negative-relative neighbor offsets.
// The frame-buffer helpers therefore return a span beginning one logical sample row before the block.
if (highBitDepth)
{
highBitDepthBlockReconstructionBuffer = this.frameBuffer.DeriveBlockPointer16((Av1Plane)plane, pixelPosition, subX, subY, out reconstructionStride);
@ -151,6 +206,8 @@ internal class Av1BlockDecoder
transformSize = transformInfo[0].Size;
Span<int> coefficients = superblockInfo.GetCoefficients((Av1Plane)plane)[this.currentCoefficientIndex[plane]..];
// Transform offsets are stored in mode-info units. Reconstruction strides are expressed in logical
// samples for both storage pipelines, so no byte scaling is applied to the high-bit-depth offset.
transformBlockOffset = ((transformInfo[0].OffsetY * reconstructionStride) + transformInfo[0].OffsetX) << Av1Constants.ModeInfoSizeLog2;
if (highBitDepth)
{
@ -224,6 +281,8 @@ internal class Av1BlockDecoder
modeInfo, coefficients, quantizationCoefficients, transformType, transformSize, (Av1Plane)plane);
if (numberOfCoefficients != 0)
{
// The packed coefficient stream prefixes every transform unit with its decoded coefficient
// count. Advance past that prefix as well as the coefficient values before the next unit.
this.currentCoefficientIndex[plane] += numberOfCoefficients + 1;
if (highBitDepth)
@ -259,6 +318,8 @@ internal class Av1BlockDecoder
// Store Luma for CFL if required!
if (plane == (int)Av1Plane.Y && StoreChromaFromLumaRequired(colorConfig, partitionInfo))
{
// The predictor span begins on the previous row; CFL storage consumes reconstructed samples from
// the transform block itself, hence the explicit one-stride advance for both sample pipelines.
if (highBitDepth)
{
this.chromaFromLumaContext.Store(
@ -285,12 +346,23 @@ internal class Av1BlockDecoder
}
}
// increment transform pointer
// Transform descriptors are stored in the same traversal order as their packed coefficient groups.
transformInfo = transformInfo[1..];
}
}
}
/// <summary>
/// Derives a byte-addressed reconstruction span beginning one row before a block.
/// </summary>
/// <param name="frameBuffer">The frame buffer containing the destination planes.</param>
/// <param name="plane">The zero-based Y, U, or V plane index.</param>
/// <param name="blockColumnInPixels">The horizontal block origin in plane samples.</param>
/// <param name="blockRowInPixels">The vertical block origin in plane samples.</param>
/// <param name="blockReconstructionBuffer">The resulting span beginning one row before the block.</param>
/// <param name="reconstructionStride">The number of logical samples between rows.</param>
/// <param name="subX">The chroma horizontal subsampling shift.</param>
/// <param name="subY">The chroma vertical subsampling shift.</param>
private static void DeriveBlockPointers(Av1FrameBuffer<byte> frameBuffer, int plane, int blockColumnInPixels, int blockRowInPixels, out Span<byte> blockReconstructionBuffer, out int reconstructionStride, int subX, int subY)
{
int blockOffset;
@ -314,13 +386,13 @@ internal class Av1BlockDecoder
break;
}
// Deviation from SVT, return PREVIOUS row in Block Reconstruction Buffer.
// Prediction addresses above samples relative to the returned span, so expose the previous row as index zero.
blockOffset -= reconstructionStride;
Guard.MustBeGreaterThanOrEqualTo(blockOffset, 0, nameof(blockOffset));
if (frameBuffer.BitDepth != Av1BitDepth.EightBit || frameBuffer.Is16BitPipeline)
{
// 16bit pipeline
// The legacy byte view represents each high-bit-depth sample with two adjacent storage elements.
blockOffset *= 2;
if (plane == 0)
{
@ -352,6 +424,14 @@ internal class Av1BlockDecoder
}
}
/// <summary>
/// Determines whether reconstructed luma samples must be retained for a later chroma-from-luma prediction.
/// </summary>
/// <param name="colorConfig">The sequence color-plane configuration.</param>
/// <param name="partitionInfo">The current block and its prediction modes.</param>
/// <returns>
/// <see langword="true"/> when chroma is present and the current luma block can contribute to a chroma-from-luma block.
/// </returns>
private static bool StoreChromaFromLumaRequired(ObuColorConfig colorConfig, Av1PartitionInfo partitionInfo)
=> !colorConfig.IsMonochrome &&
(!partitionInfo.IsChroma || partitionInfo.ModeInfo.UvMode == Av1PredictionMode.UvChromaFromLuma);

3
src/ImageSharp/Formats/Heif/Av1/Transform/Av1CoefficientShape.cs

@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Identifies how much of a transform coefficient plane the encoder evaluates.
/// </summary>
internal enum Av1CoefficientShape
{
Default,

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

@ -7,11 +7,24 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Converts spatial residual samples into AV1 transform coefficients.
/// </summary>
internal class Av1ForwardTransformer
{
/// <summary>
/// The fixed-point representation of <c>sqrt(2)</c> at <see cref="NewSqrtBitCount"/> fractional bits.
/// </summary>
private const int NewSqrt = 5793;
/// <summary>
/// The number of fractional bits used by <see cref="NewSqrt"/>.
/// </summary>
private const int NewSqrtBitCount = 12;
/// <summary>
/// Maps each concrete transform-function enum value to its managed one-dimensional implementation.
/// </summary>
private static readonly IAv1Transformer1d?[] Transformers =
[
new Av1Dct4Forward1dTransformer(),
@ -31,8 +44,20 @@ internal class Av1ForwardTransformer
null
];
/// <summary>
/// The transposed intermediate coefficient plane shared by the current encoder transform pipeline.
/// </summary>
private static readonly int[] TemporaryCoefficientsBuffer = new int[Av1Constants.MaxTransformSize * Av1Constants.MaxTransformSize];
/// <summary>
/// Resolves and applies the configured two-dimensional AV1 forward transform.
/// </summary>
/// <param name="input">The spatial residual samples.</param>
/// <param name="coefficients">The destination transform coefficients.</param>
/// <param name="stride">The number of input samples between rows.</param>
/// <param name="transformType">The compound transform type.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="bitDepth">The source sample bit depth.</param>
internal static void Transform2d(Span<short> input, Span<int> coefficients, uint stride, Av1TransformType transformType, Av1TransformSize transformSize, int bitDepth)
{
Av1Transform2dFlipConfiguration config = new(transformType, transformSize);
@ -41,6 +66,18 @@ internal class Av1ForwardTransformer
Transform2d(columnTransformer, rowTransformer, input, coefficients, stride, config, bitDepth);
}
/// <summary>
/// Applies a two-dimensional transform using explicitly selected column and row functions.
/// </summary>
/// <typeparam name="TColumn">The column-transform implementation type.</typeparam>
/// <typeparam name="TRow">The row-transform implementation type.</typeparam>
/// <param name="transformFunctionColumn">The column-transform implementation.</param>
/// <param name="transformFunctionRow">The row-transform implementation.</param>
/// <param name="input">The spatial residual samples.</param>
/// <param name="coefficients">The destination transform coefficients.</param>
/// <param name="stride">The number of input samples between rows.</param>
/// <param name="config">The per-axis transform, flip, shift, and range configuration.</param>
/// <param name="bitDepth">The source sample bit depth.</param>
internal static void Transform2d<TColumn, TRow>(TColumn? transformFunctionColumn, TRow? transformFunctionRow, Span<short> input, Span<int> coefficients, uint stride, Av1Transform2dFlipConfiguration config, int bitDepth)
where TColumn : IAv1Transformer1d
where TRow : IAv1Transformer1d
@ -55,24 +92,36 @@ internal class Av1ForwardTransformer
}
}
/// <summary>
/// Gets the managed implementation for a concrete one-dimensional transform function.
/// </summary>
/// <param name="transformerType">The concrete transform function and length.</param>
/// <returns>The transform implementation, or <see langword="null"/> for an invalid function.</returns>
private static IAv1Transformer1d? GetTransformer(Av1TransformFunctionType transformerType)
=> Transformers[(int)transformerType];
/// <summary>
/// SVT: av1_tranform_two_d_core_c
/// Applies the separable column and row stages, including normative flips, shifts, and rectangular scaling.
/// </summary>
/// <typeparam name="TColumn">The column-transform implementation type.</typeparam>
/// <typeparam name="TRow">The row-transform implementation type.</typeparam>
/// <param name="transformFunctionColumn">The column-transform implementation.</param>
/// <param name="transformFunctionRow">The row-transform implementation.</param>
/// <param name="input">The spatial residual samples.</param>
/// <param name="inputStride">The number of input samples between rows.</param>
/// <param name="output">The destination transform coefficients and temporary axis buffers.</param>
/// <param name="config">The per-axis transform, flip, shift, and range configuration.</param>
/// <param name="buf">The transposed intermediate coefficient plane.</param>
/// <param name="bitDepth">The source sample bit depth.</param>
/// <remarks>Corresponds to <c>av1_tranform_two_d_core_c</c> in the original WIP reference.</remarks>
private static void Transform2dCore<TColumn, TRow>(TColumn transformFunctionColumn, TRow transformFunctionRow, Span<short> input, uint inputStride, Span<int> output, Av1Transform2dFlipConfiguration config, Span<int> buf, int bitDepth)
where TColumn : IAv1Transformer1d
where TRow : IAv1Transformer1d
{
int c, r;
// Note when assigning txfm_size_col, we use the txfm_size from the
// row configuration and vice versa. This is intentionally done to
// accurately perform rectangular transforms. When the transform is
// rectangular, the number of columns will be the same as the
// txfm_size stored in the row cfg struct. It will make no difference
// for square transforms.
// The row configuration's size is the number of columns, while the column configuration's size is the
// number of rows. Keeping those axis names explicit is essential for rectangular transforms.
int transformColumnCount = config.TransformSize.GetWidth();
int transformRowCount = config.TransformSize.GetHeight();
int transformCount = transformColumnCount * transformRowCount;
@ -90,9 +139,8 @@ internal class Av1ForwardTransformer
int cosBitColumn = config.CosBitColumn;
int cosBitRow = config.CosBitRow;
// ASSERT(txfm_func_col != NULL);
// ASSERT(txfm_func_row != NULL);
// use output buffer as temp buffer
// Reuse the output prefix for per-axis input/output vectors. The complete transformed rows overwrite this
// scratch only after every column has been transposed into the separate intermediate buffer.
Span<int> tempInSpan = output[..transformRowCount];
Span<int> tempOutSpan = output.Slice(transformRowCount, transformRowCount);
ref int tempIn = ref tempInSpan[0];
@ -173,6 +221,12 @@ internal class Av1ForwardTransformer
}
}
/// <summary>
/// Applies a signed fixed-point shift to a contiguous transform-stage vector.
/// </summary>
/// <param name="arr">A reference to the first transform-stage value.</param>
/// <param name="size">The number of values to update.</param>
/// <param name="bit">A positive rounded-right shift or a negative exact-left shift.</param>
private static void RoundShiftArray(ref int arr, int size, int bit)
{
if (bit == 0)
@ -202,8 +256,12 @@ internal class Av1ForwardTransformer
}
/// <summary>
/// SVT: get_rect_tx_log_ratio
/// Gets the signed base-two ratio between transform columns and rows.
/// </summary>
/// <param name="col">The transform width.</param>
/// <param name="row">The transform height.</param>
/// <returns>Zero for square transforms, positive when wider, or negative when taller.</returns>
/// <remarks>Corresponds to <c>get_rect_tx_log_ratio</c> in the original WIP reference.</remarks>
public static int GetRectangularRatio(int col, int row)
{
if (col == row)

64
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformerFactory.cs

@ -5,8 +5,24 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Selects the forward-transform coefficient shape used by AV1 encoder mode decision.
/// </summary>
internal static class Av1ForwardTransformerFactory
{
/// <summary>
/// Applies the encoder-selected coefficient-shape transform to a residual block.
/// </summary>
/// <param name="residualBuffer">The spatial residual samples.</param>
/// <param name="residualStride">The number of residual samples between rows.</param>
/// <param name="coefficientBuffer">The destination transform coefficients.</param>
/// <param name="coefficientStride">The number of coefficient positions between rows.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="threeQuadEnergy">The accumulated energy outside the retained coefficient shape.</param>
/// <param name="bitDepth">The source sample bit depth.</param>
/// <param name="transformType">The compound transform type.</param>
/// <param name="componentType">The luma or chroma component class.</param>
/// <param name="transformCoefficientShape">The subset of coefficients evaluated by mode decision.</param>
internal static void EstimateTransform(
Span<short> residualBuffer,
uint residualStride,
@ -36,6 +52,18 @@ internal static class Av1ForwardTransformerFactory
}
}
/// <summary>
/// Applies the complete two-dimensional transform without discarding coefficients.
/// </summary>
/// <param name="residualBuffer">The spatial residual samples.</param>
/// <param name="residualStride">The number of residual samples between rows.</param>
/// <param name="coefficientBuffer">The destination transform coefficients.</param>
/// <param name="coefficientStride">The number of coefficient positions between rows.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="threeQuadEnergy">The accumulated energy outside the retained coefficient shape.</param>
/// <param name="bitDepth">The source sample bit depth.</param>
/// <param name="transformType">The compound transform type.</param>
/// <param name="componentType">The luma or chroma component class.</param>
private static void EstimateTransformDefault(
Span<short> residualBuffer,
uint residualStride,
@ -48,9 +76,45 @@ internal static class Av1ForwardTransformerFactory
Av1PlaneType componentType)
=> Av1ForwardTransformer.Transform2d(residualBuffer, coefficientBuffer, residualStride, transformType, transformSize, bitDepth);
/// <summary>
/// Applies the half-coefficient transform shape and measures the discarded coefficient energy.
/// </summary>
/// <param name="residualBuffer">The spatial residual samples.</param>
/// <param name="residualStride">The number of residual samples between rows.</param>
/// <param name="coefficientBuffer">The destination transform coefficients.</param>
/// <param name="coefficientStride">The number of coefficient positions between rows.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="threeQuadEnergy">The accumulated energy outside the retained coefficient shape.</param>
/// <param name="bitDepth">The source sample bit depth.</param>
/// <param name="transformType">The compound transform type.</param>
/// <param name="componentType">The luma or chroma component class.</param>
private static void EstimateTransformN2(Span<short> residualBuffer, uint residualStride, Span<int> coefficientBuffer, uint coefficientStride, Av1TransformSize transformSize, ref ulong threeQuadEnergy, int bitDepth, Av1TransformType transformType, Av1PlaneType componentType) => throw new NotImplementedException();
/// <summary>
/// Applies the quarter-coefficient transform shape and measures the discarded coefficient energy.
/// </summary>
/// <param name="residualBuffer">The spatial residual samples.</param>
/// <param name="residualStride">The number of residual samples between rows.</param>
/// <param name="coefficientBuffer">The destination transform coefficients.</param>
/// <param name="coefficientStride">The number of coefficient positions between rows.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="threeQuadEnergy">The accumulated energy outside the retained coefficient shape.</param>
/// <param name="bitDepth">The source sample bit depth.</param>
/// <param name="transformType">The compound transform type.</param>
/// <param name="componentType">The luma or chroma component class.</param>
private static void EstimateTransformN4(Span<short> residualBuffer, uint residualStride, Span<int> coefficientBuffer, uint coefficientStride, Av1TransformSize transformSize, ref ulong threeQuadEnergy, int bitDepth, Av1TransformType transformType, Av1PlaneType componentType) => throw new NotImplementedException();
/// <summary>
/// Evaluates only the transform's DC coefficient and measures the discarded coefficient energy.
/// </summary>
/// <param name="residualBuffer">The spatial residual samples.</param>
/// <param name="residualStride">The number of residual samples between rows.</param>
/// <param name="coefficientBuffer">The destination transform coefficients.</param>
/// <param name="coefficientStride">The number of coefficient positions between rows.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="threeQuadEnergy">The accumulated energy outside the retained coefficient shape.</param>
/// <param name="bitDepth">The source sample bit depth.</param>
/// <param name="transformType">The compound transform type.</param>
/// <param name="componentType">The luma or chroma component class.</param>
private static void EstimateTransformOnlyDc(Span<short> residualBuffer, uint residualStride, Span<int> coefficientBuffer, uint coefficientStride, Av1TransformSize transformSize, ref ulong threeQuadEnergy, int bitDepth, Av1TransformType transformType, Av1PlaneType componentType) => throw new NotImplementedException();
}

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

@ -5,13 +5,28 @@ using System.ComponentModel;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Applies separable two-dimensional AV1 inverse transforms and adds their residuals to predicted samples.
/// </summary>
internal class Av1Inverse2dTransformer
{
/// <summary>
/// The lossless Walsh-Hadamard coefficient shift used by the retained reference implementation.
/// </summary>
private const int UnitQuantizationShift = 2;
/// <summary>
/// SVT: inv_txfm2d_add_c
/// Applies a separable inverse transform and adds its residual to high-bit-depth predicted samples.
/// </summary>
/// <param name="input">The dequantized coefficients in raster order.</param>
/// <param name="outputForRead">The predicted samples read by reconstruction.</param>
/// <param name="strideForRead">The number of read samples between rows.</param>
/// <param name="outputForWrite">The destination reconstructed samples.</param>
/// <param name="strideForWrite">The number of destination samples between rows.</param>
/// <param name="config">The per-axis transform, flip, shift, and range configuration.</param>
/// <param name="transformFunctionBuffer">The intermediate plane and two per-axis scratch vectors.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <remarks>Corresponds to <c>inv_txfm2d_add_c</c> in the original WIP reference.</remarks>
internal static void Transform2dAdd(
Span<int> input,
Span<short> outputForRead,
@ -22,12 +37,8 @@ internal class Av1Inverse2dTransformer
Span<int> transformFunctionBuffer,
int bitDepth)
{
// Note when assigning txfm_size_col, we use the txfm_size from the
// row configuration and vice versa. This is intentionally done to
// accurately perform rectangular transforms. When the transform is
// rectangular, the number of columns will be the same as the
// txfm_size stored in the row cfg struct. It will make no difference
// for square transforms.
// The row configuration's size is the transform width, while the column configuration's size is its height.
// Keeping those axis names explicit is essential for rectangular transforms.
int transformWidth = config.TransformSize.GetWidth();
int transformHeight = config.TransformSize.GetHeight();
@ -43,8 +54,8 @@ internal class Av1Inverse2dTransformer
Guard.NotNull(functionColumn);
Guard.NotNull(functionRow);
// txfm_buf's length is txfm_size_row * txfm_size_col + 2 * MAX(txfm_size_row, txfm_size_col)
// it is used for intermediate data buffering
// Partition the caller-provided buffer into a full intermediate plane and two vectors sized for the longer
// axis. This avoids allocating within each reconstructed transform block.
int bufferOffset = Math.Max(transformHeight, transformWidth);
Guard.MustBeSizedAtLeast(transformFunctionBuffer, (transformHeight * transformWidth) + (2 * bufferOffset), nameof(transformFunctionBuffer));
Span<int> tempIn = transformFunctionBuffer;
@ -139,8 +150,16 @@ internal class Av1Inverse2dTransformer
}
/// <summary>
/// SVT: inv_txfm2d_add_c
/// Applies a separable inverse transform and adds its residual to eight-bit predicted samples.
/// </summary>
/// <param name="input">The dequantized coefficients in raster order.</param>
/// <param name="outputForRead">The predicted samples read by reconstruction.</param>
/// <param name="strideForRead">The number of read samples between rows.</param>
/// <param name="outputForWrite">The destination reconstructed samples.</param>
/// <param name="strideForWrite">The number of destination samples between rows.</param>
/// <param name="config">The per-axis transform, flip, shift, and range configuration.</param>
/// <param name="transformFunctionBuffer">The intermediate plane and two per-axis scratch vectors.</param>
/// <remarks>Corresponds to <c>inv_txfm2d_add_c</c> in the original WIP reference.</remarks>
internal static void Transform2dAdd(
Span<int> input,
Span<byte> outputForRead,
@ -152,12 +171,8 @@ internal class Av1Inverse2dTransformer
{
const int bitDepth = 8;
// Note when assigning txfm_size_col, we use the txfm_size from the
// row configuration and vice versa. This is intentionally done to
// accurately perform rectangular transforms. When the transform is
// rectangular, the number of columns will be the same as the
// txfm_size stored in the row cfg struct. It will make no difference
// for square transforms.
// The row configuration's size is the transform width, while the column configuration's size is its height.
// Keeping those axis names explicit is essential for rectangular transforms.
int transformWidth = config.TransformSize.GetWidth();
int transformHeight = config.TransformSize.GetHeight();
@ -173,8 +188,8 @@ internal class Av1Inverse2dTransformer
Guard.NotNull(functionColumn);
Guard.NotNull(functionRow);
// txfm_buf's length is txfm_size_row * txfm_size_col + 2 * MAX(txfm_size_row, txfm_size_col)
// it is used for intermediate data buffering
// Partition the caller-provided buffer into a full intermediate plane and two vectors sized for the longer
// axis. This avoids allocating within each reconstructed transform block.
int bufferOffset = Math.Max(transformHeight, transformWidth);
Guard.MustBeSizedAtLeast(transformFunctionBuffer, (transformHeight * transformWidth) + (2 * bufferOffset), nameof(transformFunctionBuffer));
Span<int> tempIn = transformFunctionBuffer;

108
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformMath.cs

@ -5,11 +5,24 @@ using System;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Contains fixed-point constants and quantizer lookup tables shared by AV1 inverse transforms.
/// </summary>
internal static class Av1InverseTransformMath
{
/// <summary>
/// The fixed-point representation of <c>1 / sqrt(2)</c> at <see cref="NewSqrt2BitCount"/> fractional bits.
/// </summary>
public const int NewInverseSqrt2 = 2896;
/// <summary>
/// The number of fractional bits used by <see cref="NewInverseSqrt2"/>.
/// </summary>
public const int NewSqrt2BitCount = 12;
/// <summary>
/// Gets the normative AC dequantizer value indexed by bit-depth category and quantizer index.
/// </summary>
public static readonly int[,] AcQLookup = new int[3, 256]
{
{
@ -64,6 +77,9 @@ internal static class Av1InverseTransformMath
}
};
/// <summary>
/// Contains the normative DC dequantizer values indexed by bit-depth category and quantizer index.
/// </summary>
private static readonly int[,] DcQLookup = new int[3, 256]
{
{
@ -117,25 +133,49 @@ internal static class Av1InverseTransformMath
}
};
/// <summary>
/// Gets a clipped DC dequantizer value.
/// </summary>
/// <param name="qIndex">The segment quantizer index.</param>
/// <param name="delta">The plane-specific DC quantizer delta.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <returns>The DC dequantizer value.</returns>
public static int GetDcQuantization(int qIndex, int delta, Av1BitDepth bitDepth)
=> DcQLookup[(int)bitDepth, Av1Math.Clip3(0, 255, qIndex + delta)];
/// <summary>
/// Gets a clipped AC dequantizer value.
/// </summary>
/// <param name="qIndex">The segment quantizer index.</param>
/// <param name="delta">The plane-specific AC quantizer delta.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <returns>The AC dequantizer value.</returns>
public static int GetAcQuantization(int qIndex, int delta, Av1BitDepth bitDepth)
=> AcQLookup[(int)bitDepth, Av1Math.Clip3(0, 255, qIndex + delta)];
/// <summary>
/// Gets the encoder zero-bin factor selected by quantizer magnitude and sample bit depth.
/// </summary>
/// <param name="q">The base quantizer index.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <returns>The zero-bin factor.</returns>
public static int GetQzbinFactor(int q, Av1BitDepth bitDepth)
{
int quant = GetDcQuantization(q, 0, bitDepth);
// Bit hack to get to:
// EightBit => 148
// TenBit => 592
// TwelveBit => 2368
// Scaling the eight-bit threshold by four for every two added sample bits preserves the quantizer decision
// at equal normalized signal levels: 148 for 8-bit, 592 for 10-bit, and 2368 for 12-bit.
int shift = (int)bitDepth << 1;
int threshold = (1 << shift) * 148;
return q == 0 ? 64 : (quant < threshold ? 84 : 80);
}
/// <summary>
/// Computes the fixed-point multiplier and shift used to replace division by a quantizer.
/// </summary>
/// <param name="quantization">Receives the reciprocal multiplier without its implicit leading bit.</param>
/// <param name="shift">Receives the reciprocal scaling shift.</param>
/// <param name="d">The positive quantizer divisor.</param>
public static void InvertQuantization(out int quantization, out int shift, int d)
{
uint t;
@ -151,18 +191,37 @@ internal static class Av1InverseTransformMath
shift = 1 << (16 - l);
}
/// <summary>
/// Adds an inverse-transform residual to an eight-bit predicted sample and clips the result.
/// </summary>
/// <param name="dest">The predicted sample.</param>
/// <param name="trans">The inverse-transform residual.</param>
/// <returns>The reconstructed eight-bit sample.</returns>
public static byte ClipPixelAdd(byte dest, long trans)
{
trans = CheckRange(trans, 8);
return (byte)ClipPixelHighBitDepth(dest + trans, 8);
}
/// <summary>
/// Adds an inverse-transform residual to a high-bit-depth predicted sample and clips the result.
/// </summary>
/// <param name="dest">The predicted sample stored in the signed transform representation.</param>
/// <param name="trans">The inverse-transform residual.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <returns>The reconstructed sample stored in the signed transform representation.</returns>
public static short ClipPixelAdd(short dest, long trans, int bitDepth)
{
trans = CheckRange(trans, bitDepth);
return ClipPixelHighBitDepth(dest + trans, bitDepth);
}
/// <summary>
/// Clips a reconstructed sample to the unsigned range selected by its bit depth.
/// </summary>
/// <param name="val">The unclipped reconstructed sample.</param>
/// <param name="bd">The coded sample bit depth.</param>
/// <returns>The clipped sample stored in the signed transform representation.</returns>
private static short ClipPixelHighBitDepth(long val, int bd) => bd switch
{
10 => (short)Av1Math.Clamp(val, 0, 1023),
@ -170,6 +229,12 @@ internal static class Av1InverseTransformMath
_ => (short)Av1Math.Clamp(val, 0, 255),
};
/// <summary>
/// Applies a signed fixed-point shift to the requested prefix of an integer buffer.
/// </summary>
/// <param name="arr">The transform-stage values.</param>
/// <param name="size">The number of values to update.</param>
/// <param name="bit">A positive rounded-right shift or a negative exact-left shift.</param>
public static void RoundShiftArray(Span<int> arr, int size, int bit)
{
int i;
@ -196,6 +261,12 @@ internal static class Av1InverseTransformMath
}
}
/// <summary>
/// Clamps a transform-stage buffer to a signed range of the specified bit width.
/// </summary>
/// <param name="buffer">The transform-stage values.</param>
/// <param name="size">The number of values to clamp.</param>
/// <param name="bit">The signed range width in bits.</param>
internal static void ClampBuffer(Span<int> buffer, int size, byte bit)
{
for (int i = 0; i < size; i++)
@ -204,6 +275,12 @@ internal static class Av1InverseTransformMath
}
}
/// <summary>
/// Clamps one transform-stage value to a signed range of the specified bit width.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="bit">The signed range width in bits.</param>
/// <returns>The clamped value.</returns>
private static int ClampValue(int value, byte bit)
{
if (bit <= 0)
@ -211,11 +288,17 @@ internal static class Av1InverseTransformMath
return value; // Do nothing for invalid clamp bit.
}
long max_value = (1L << (bit - 1)) - 1;
long min_value = -(1L << (bit - 1));
return (int)Av1Math.Clamp(value, min_value, max_value);
long maximum = (1L << (bit - 1)) - 1;
long minimum = -(1L << (bit - 1));
return (int)Av1Math.Clamp(value, minimum, maximum);
}
/// <summary>
/// Restricts an inverse-transform residual to the intermediate range permitted for the sample bit depth.
/// </summary>
/// <param name="input">The inverse-transform residual.</param>
/// <param name="bd">The coded sample bit depth.</param>
/// <returns>The range-limited residual.</returns>
private static long CheckRange(long input, int bd)
{
// AV1 TX case
@ -223,11 +306,16 @@ internal static class Av1InverseTransformMath
// - 10 bit: signed 18 bit integer
// - 12 bit: signed 20 bit integer
// - max quantization error = 1828 << (bd - 8)
int int_max = (1 << (7 + bd)) - 1 + (914 << (bd - 7));
int int_min = -int_max - 1;
return Av1Math.Clamp(input, int_min, int_max);
int maximum = (1 << (7 + bd)) - 1 + (914 << (bd - 7));
int minimum = -maximum - 1;
return Av1Math.Clamp(input, minimum, maximum);
}
/// <summary>
/// Gets the maximum coded coefficient count retained for a transform size.
/// </summary>
/// <param name="transformSize">The signaled transform size.</param>
/// <returns>The maximum coefficient end position represented by AV1 syntax.</returns>
internal static int GetMaxEndOfBuffer(Av1TransformSize transformSize)
{
if (transformSize is Av1TransformSize.Size64x64 or Av1TransformSize.Size64x32 or Av1TransformSize.Size32x64)

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

@ -3,11 +3,23 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Reconstructs decoded AV1 transform coefficients into prediction sample buffers.
/// </summary>
internal class Av1InverseTransformer
{
/// <summary>
/// SVT: svt_aom_inv_transform_recon8bit
/// Reconstructs an eight-bit transform block in place by adding its inverse-transform residual.
/// </summary>
/// <param name="coefficientsBuffer">The dequantized transform coefficients.</param>
/// <param name="reconstructionBuffer">The predicted samples and reconstruction destination.</param>
/// <param name="reconstructionStride">The number of samples between rows.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="transformType">The compound transform type.</param>
/// <param name="plane">The zero-based Y, U, or V plane index.</param>
/// <param name="numberOfCoefficients">The decoded coefficient end position.</param>
/// <param name="isLossless">Whether the segment uses lossless transform rules.</param>
/// <remarks>Corresponds to <c>svt_aom_inv_transform_recon8bit</c> in the original WIP reference.</remarks>
public static void Reconstruct8Bit(Span<int> coefficientsBuffer, Span<byte> reconstructionBuffer, int reconstructionStride, Av1TransformSize transformSize, Av1TransformType transformType, int plane, int numberOfCoefficients, bool isLossless)
{
Av1TransformFunctionParameters transformFunctionParameters = new()
@ -25,8 +37,19 @@ internal class Av1InverseTransformer
}
/// <summary>
/// SVT: svt_aom_inv_transform_recon8bit
/// Reconstructs an eight-bit transform block from a separate prediction buffer.
/// </summary>
/// <param name="coefficientsBuffer">The dequantized transform coefficients.</param>
/// <param name="reconstructionBufferRead">The predicted samples read by reconstruction.</param>
/// <param name="reconstructionReadStride">The number of prediction samples between rows.</param>
/// <param name="reconstructionBufferWrite">The destination reconstructed samples.</param>
/// <param name="reconstructionWriteStride">The number of destination samples between rows.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="transformType">The compound transform type.</param>
/// <param name="plane">The zero-based Y, U, or V plane index.</param>
/// <param name="numberOfCoefficients">The decoded coefficient end position.</param>
/// <param name="isLossless">Whether the segment uses lossless transform rules.</param>
/// <remarks>Corresponds to <c>svt_aom_inv_transform_recon8bit</c> in the original WIP reference.</remarks>
public static void Reconstruct8Bit(Span<int> coefficientsBuffer, Span<byte> reconstructionBufferRead, int reconstructionReadStride, Span<byte> reconstructionBufferWrite, int reconstructionWriteStride, Av1TransformSize transformSize, Av1TransformType transformType, int plane, int numberOfCoefficients, bool isLossless)
{
Av1TransformFunctionParameters transformFunctionParameters = new()
@ -39,9 +62,8 @@ internal class Av1InverseTransformer
Is16BitPipeline = false
};
/* When output pointers to read and write are differents,
* then kernel copy also all buffer from read to write,
* and cannot be limited by End Of Buffer calculations. */
// Separate prediction and destination buffers require every sample to be copied or reconstructed. Restricting
// traversal to the coded coefficient end position would leave the untouched prediction region unwritten.
transformFunctionParameters.EndOfBuffer = Av1InverseTransformMath.GetMaxEndOfBuffer(transformSize);
Av1InverseTransformerFactory.InverseTransformAdd(
@ -49,8 +71,18 @@ internal class Av1InverseTransformer
}
/// <summary>
/// AV1: 7.11.2 Reconstruct.
/// Reconstructs a high-bit-depth transform block in place by adding its inverse-transform residual.
/// </summary>
/// <param name="coefficientsBuffer">The dequantized transform coefficients.</param>
/// <param name="reconstructionBuffer">The predicted samples and reconstruction destination.</param>
/// <param name="reconstructionStride">The number of logical samples between rows.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="transformType">The compound transform type.</param>
/// <param name="plane">The zero-based Y, U, or V plane index.</param>
/// <param name="numberOfCoefficients">The decoded coefficient end position.</param>
/// <param name="isLossless">Whether the segment uses lossless transform rules.</param>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <remarks>Implements the reconstruction operation in AV1 section 7.11.2.</remarks>
public static void ReconstructHighBitDepth(Span<int> coefficientsBuffer, Span<short> reconstructionBuffer, int reconstructionStride, Av1TransformSize transformSize, Av1TransformType transformType, int plane, int numberOfCoefficients, bool isLossless, Av1BitDepth bitDepth)
{
Av1TransformFunctionParameters transformFunctionParameters = new()

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

@ -5,32 +5,60 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Selects and runs the AV1 inverse-transform pipeline for byte or high-bit-depth sample storage.
/// </summary>
internal static class Av1InverseTransformerFactory
{
/// <summary>
/// SVT: svt_av1_inv_txfm_add
/// Applies an inverse transform and adds its residual to eight-bit predicted samples.
/// </summary>
/// <param name="coefficients">The dequantized transform coefficients.</param>
/// <param name="readBuffer">The predicted samples read by reconstruction.</param>
/// <param name="readStride">The number of read samples between rows.</param>
/// <param name="writeBuffer">The destination reconstructed samples.</param>
/// <param name="writeStride">The number of destination samples between rows.</param>
/// <param name="transformFunctionParameters">The transform type, dimensions, bit depth, and pipeline selection.</param>
/// <remarks>Corresponds to <c>svt_av1_inv_txfm_add</c> in the original WIP reference.</remarks>
public static unsafe void InverseTransformAdd(Span<int> coefficients, Span<byte> readBuffer, int readStride, Span<byte> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters)
{
Guard.MustBeLessThanOrEqualTo(transformFunctionParameters.BitDepth, 8, nameof(transformFunctionParameters));
Guard.IsFalse(transformFunctionParameters.Is16BitPipeline, nameof(transformFunctionParameters), "Calling 8-bit pipeline while 16-bit is requested.");
int width = transformFunctionParameters.TransformSize.GetWidth();
int height = transformFunctionParameters.TransformSize.GetHeight();
// The 2-D transform needs one complete intermediate plane plus one input and output vector for the longer axis.
Span<int> buffer = new int[(width * height) + (2 * Math.Max(width, height))];
Av1Transform2dFlipConfiguration config = new(transformFunctionParameters.TransformType, transformFunctionParameters.TransformSize);
Av1Inverse2dTransformer.Transform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, config, buffer);
}
/// <summary>
/// Applies an inverse transform and adds its residual to high-bit-depth predicted samples.
/// </summary>
/// <param name="coefficients">The dequantized transform coefficients.</param>
/// <param name="readBuffer">The predicted samples read by reconstruction.</param>
/// <param name="readStride">The number of read samples between rows.</param>
/// <param name="writeBuffer">The destination reconstructed samples.</param>
/// <param name="writeStride">The number of destination samples between rows.</param>
/// <param name="transformFunctionParameters">The transform type, dimensions, bit depth, and pipeline selection.</param>
public static unsafe void InverseTransformAdd(Span<int> coefficients, Span<short> readBuffer, int readStride, Span<short> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters)
{
Guard.IsTrue(transformFunctionParameters.Is16BitPipeline, nameof(transformFunctionParameters), "Calling 16-bit pipeline while 8-bit is requested.");
int width = transformFunctionParameters.TransformSize.GetWidth();
int height = transformFunctionParameters.TransformSize.GetHeight();
// The 2-D transform needs one complete intermediate plane plus one input and output vector for the longer axis.
Span<int> buffer = new int[(width * height) + (2 * Math.Max(width, height))];
Av1Transform2dFlipConfiguration config = new(transformFunctionParameters.TransformType, transformFunctionParameters.TransformSize);
Av1Inverse2dTransformer.Transform2dAdd(coefficients, readBuffer, readStride, writeBuffer, writeStride, config, buffer, transformFunctionParameters.BitDepth);
}
/// <summary>
/// Creates the inverse-transform implementation for a concrete function and length.
/// </summary>
/// <param name="type">The concrete transform function.</param>
/// <returns>The transform implementation, or <see langword="null"/> for an invalid function.</returns>
internal static IAv1Transformer1d? GetTransformer(Av1TransformFunctionType type) => type switch
{
Av1TransformFunctionType.Dct4 => new Av1Dct4Inverse1dTransformer(),

33
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrder.cs

@ -3,12 +3,30 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Describes the forward scan, inverse scan, and entropy-neighbor mapping for an AV1 transform size.
/// </summary>
internal readonly struct Av1ScanOrder
{
/// <summary>
/// The coefficient positions in coded traversal order.
/// </summary>
private readonly short[] scan;
/// <summary>
/// The coded position of each raster-order coefficient.
/// </summary>
private readonly short[] inverseScan;
/// <summary>
/// The coefficient-neighbor mapping used to derive entropy contexts.
/// </summary>
private readonly short[] neighbors;
/// <summary>
/// Initializes a new instance of the <see cref="Av1ScanOrder"/> struct when only coefficient traversal is required.
/// </summary>
/// <param name="scan">The coefficient positions in coded traversal order.</param>
public Av1ScanOrder(short[] scan)
{
this.scan = scan;
@ -16,6 +34,12 @@ internal readonly struct Av1ScanOrder
this.neighbors = [];
}
/// <summary>
/// Initializes a new instance of the <see cref="Av1ScanOrder"/> struct with complete entropy-context mappings.
/// </summary>
/// <param name="scan">The coefficient positions in coded traversal order.</param>
/// <param name="inverseScan">The coded position of each raster-order coefficient.</param>
/// <param name="neighbors">The coefficient neighbors used to derive entropy contexts.</param>
public Av1ScanOrder(short[] scan, short[] inverseScan, short[] neighbors)
{
this.scan = scan;
@ -23,9 +47,18 @@ internal readonly struct Av1ScanOrder
this.neighbors = neighbors;
}
/// <summary>
/// Gets the coefficient positions in coded traversal order.
/// </summary>
public ReadOnlySpan<short> Scan => this.scan;
/// <summary>
/// Gets the coded position of each raster-order coefficient.
/// </summary>
public ReadOnlySpan<short> InverseScan => this.inverseScan;
/// <summary>
/// Gets the coefficient-neighbor mapping used for entropy contexts.
/// </summary>
public ReadOnlySpan<short> Neighbors => this.neighbors;
}

19
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ScanOrderConstants.cs

@ -3,9 +3,19 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Contains the normative coefficient scan orders and quantization-matrix dimensions for AV1 transform blocks.
/// </summary>
internal static class Av1ScanOrderConstants
{
/// <summary>
/// The number of bits used to signal a quantization-matrix level.
/// </summary>
public const int QuantizationMatrixLevelBitCount = 4;
/// <summary>
/// The number of quantization-matrix levels, including the flat matrix.
/// </summary>
public const int QuantizationMatrixLevelCount = 1 << QuantizationMatrixLevelBitCount;
private static readonly short[] DefaultScan4x4 = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
@ -446,6 +456,9 @@ internal static class Av1ScanOrderConstants
private static readonly short[] MatrixRowScan8x32Neighbors = [];
private static readonly short[] MatrixRowScan32x8Neighbors = [];
/// <summary>
/// Maps transform size and compound transform type to coefficient and entropy-neighbor traversal tables.
/// </summary>
private static readonly Av1ScanOrder[][] ScanOrders =
[
@ -840,6 +853,12 @@ internal static class Av1ScanOrderConstants
]
];
/// <summary>
/// Gets the coefficient traversal and entropy-neighbor mappings for a transform block.
/// </summary>
/// <param name="transformSize">The transform-block dimensions.</param>
/// <param name="transformType">The compound transform type.</param>
/// <returns>The selected scan order.</returns>
public static Av1ScanOrder GetScanOrder(Av1TransformSize transformSize, Av1TransformType transformType)
=> ScanOrders[(int)transformSize][(int)transformType];
}

45
src/ImageSharp/Formats/Heif/Av1/Transform/Av1SinusConstants.cs

@ -3,11 +3,22 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Contains the fixed-point sine and cosine tables used by the normative AV1 transform stages.
/// </summary>
internal static class Av1SinusConstants
{
/// <summary>
/// The smallest supported number of fractional bits in a cosine lookup table.
/// </summary>
public const int MinimumCosinusBit = 10;
// av1_cospi_arr[i][j] = (int32_t)round(cos(M_PI*j/128) * (1<<(cos_bit_min+i)));
/// <summary>
/// Fixed-point cosine values indexed by precision minus <see cref="MinimumCosinusBit"/> and angle step.
/// </summary>
/// <remarks>
/// Each value is <c>round(cos(pi * angle / 128) * 2^precision)</c>.
/// </remarks>
private static readonly int[][] CosinusPiArray =
[
[
@ -54,8 +65,13 @@ internal static class Av1SinusConstants
]
];
// svt_aom_eb_av1_sinpi_arr_data[i][j] = (int32_t)round((sqrt(2) * sin(j*Pi/9) * 2 / 3) * (1
// << (cos_bit_min + i))) modified so that elements j=1,2 sum to element j=4.
/// <summary>
/// Fixed-point sine values indexed by precision minus <see cref="MinimumCosinusBit"/> and angle step.
/// </summary>
/// <remarks>
/// Values follow <c>round((sqrt(2) * sin(angle * pi / 9) * 2 / 3) * 2^precision)</c>, adjusted so
/// the first two nonzero elements sum to the fourth.
/// </remarks>
private static readonly int[][] SinusPiArray =
[
[0, 330, 621, 836, 951],
@ -67,6 +83,9 @@ internal static class Av1SinusConstants
[0, 21133, 39716, 53510, 60849]
];
/// <summary>
/// One quadrant of the signed cosine table used by directional intra prediction.
/// </summary>
private static readonly int[] Cosinus128Lookup = [
4096, 4095, 4091, 4085, 4076, 4065, 4052, 4036,
4017, 3996, 3973, 3948, 3920, 3889, 3857, 3822,
@ -78,18 +97,38 @@ internal static class Av1SinusConstants
799, 700, 601, 501, 401, 301, 201, 101, 0
];
/// <summary>
/// Gets the transform cosine table for a fixed-point precision.
/// </summary>
/// <param name="n">The number of fractional bits.</param>
/// <returns>The cosine table for the requested precision.</returns>
public static Span<int> CosinusPi(int n) => CosinusPiArray[n - MinimumCosinusBit];
/// <summary>
/// Gets the transform sine table for a fixed-point precision.
/// </summary>
/// <param name="n">The number of fractional bits.</param>
/// <returns>The sine table for the requested precision.</returns>
public static Span<int> SinusPi(int n) => SinusPiArray[n - MinimumCosinusBit];
/// <summary>
/// Spec: 7.13.2.1 Butterfly functions
/// </summary>
/// <summary>
/// Gets a directional-prediction sine value for an angle in 128-step circle units.
/// </summary>
/// <param name="angle">The signed angle.</param>
/// <returns>The signed fixed-point sine value.</returns>
public static int Sinus128(int angle) => Cosinus128(angle - 64);
/// <summary>
/// Spec: 7.13.2.1 Butterfly functions
/// </summary>
/// <summary>
/// Gets a directional-prediction cosine value for an angle in 128-step circle units.
/// </summary>
/// <param name="angle">The signed angle.</param>
/// <returns>The signed fixed-point cosine value.</returns>
public static int Cosinus128(int angle)
{
int angle2 = angle & 255;

145
src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs

@ -3,11 +3,24 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Resolves an AV1 compound transform into its per-axis functions, flips, shifts, and stage ranges.
/// </summary>
internal class Av1Transform2dFlipConfiguration
{
/// <summary>
/// The maximum number of fixed-point stages in any supported one-dimensional transform.
/// </summary>
public const int MaxStageNumber = 12;
/// <summary>
/// The base-two logarithm of the smallest supported transform dimension.
/// </summary>
private const int SmallestTransformSizeLog2 = 2;
/// <summary>
/// Maps each compound transform type to the function applied down the transform columns.
/// </summary>
private static readonly Av1TransformType1d[] VerticalType =
[
Av1TransformType1d.Dct,
@ -28,6 +41,9 @@ internal class Av1Transform2dFlipConfiguration
Av1TransformType1d.Identity,
];
/// <summary>
/// Maps each compound transform type to the function applied across the transform rows.
/// </summary>
private static readonly Av1TransformType1d[] HorizontalType =
[
Av1TransformType1d.Dct,
@ -48,6 +64,9 @@ internal class Av1Transform2dFlipConfiguration
Av1TransformType1d.FlipAdst,
];
/// <summary>
/// Contains the three normative fixed-point shifts for every transform size.
/// </summary>
private static readonly int[][] ShiftMap =
[
[2, 0, 0], // 4x4
@ -71,12 +90,21 @@ internal class Av1Transform2dFlipConfiguration
[2, -4, 0], // 64x16
];
/// <summary>
/// Selects column-transform cosine precision by width and height logarithm.
/// </summary>
private static readonly int[][] CosBitColumnMap =
[[13, 13, 13, 0, 0], [13, 13, 13, 12, 0], [13, 13, 13, 12, 13], [0, 13, 13, 12, 13], [0, 0, 13, 12, 13]];
/// <summary>
/// Selects row-transform cosine precision by width and height logarithm.
/// </summary>
private static readonly int[][] CosBitRowMap =
[[13, 13, 12, 0, 0], [13, 13, 13, 12, 0], [13, 13, 12, 13, 12], [0, 12, 13, 12, 11], [0, 0, 12, 11, 10]];
/// <summary>
/// Maps a transform dimension and one-dimensional type to its concrete staged function.
/// </summary>
private static readonly Av1TransformFunctionType[][] TransformFunctionTypeMap =
[
[Av1TransformFunctionType.Dct4, Av1TransformFunctionType.Adst4, Av1TransformFunctionType.Adst4, Av1TransformFunctionType.Identity4],
@ -86,6 +114,9 @@ internal class Av1Transform2dFlipConfiguration
[Av1TransformFunctionType.Dct64, Av1TransformFunctionType.Invalid, Av1TransformFunctionType.Invalid, Av1TransformFunctionType.Identity64]
];
/// <summary>
/// Contains the number of fixed-point stages executed by each concrete transform function.
/// </summary>
private static readonly int[] StageNumberList =
[
4, // TXFM_TYPE_DCT4
@ -104,6 +135,9 @@ internal class Av1Transform2dFlipConfiguration
1, // TXFM_TYPE_IDENTITY64
];
/// <summary>
/// Contains twice the non-scaled bit range required after every transform stage.
/// </summary>
private static readonly int[][] RangeMulti2List =
[
[0, 2, 3, 3], // fdct4_range_mult2
@ -122,8 +156,16 @@ internal class Av1Transform2dFlipConfiguration
[5], // fidtx64_range_mult2
];
/// <summary>
/// The three fixed-point shifts applied before the column transform, between axes, and after the row transform.
/// </summary>
private int[] shift;
/// <summary>
/// Initializes a new instance of the <see cref="Av1Transform2dFlipConfiguration"/> class.
/// </summary>
/// <param name="transformType">The compound horizontal and vertical transform type.</param>
/// <param name="transformSize">The transform-block dimensions.</param>
public Av1Transform2dFlipConfiguration(Av1TransformType transformType, Av1TransformSize transformSize)
{
// SVT: svt_av1_get_inv_txfm_cfg
@ -133,13 +175,13 @@ internal class Av1Transform2dFlipConfiguration
this.SetFlip(transformType);
this.TransformTypeColumn = VerticalType[(int)transformType];
this.TransformTypeRow = HorizontalType[(int)transformType];
int txw_idx = transformSize.GetBlockWidthLog2() - SmallestTransformSizeLog2;
int txh_idx = transformSize.GetBlockHeightLog2() - SmallestTransformSizeLog2;
int transformWidthIndex = transformSize.GetBlockWidthLog2() - SmallestTransformSizeLog2;
int transformHeightIndex = transformSize.GetBlockHeightLog2() - SmallestTransformSizeLog2;
this.shift = ShiftMap[(int)transformSize];
this.CosBitColumn = CosBitColumnMap[txw_idx][txh_idx];
this.CosBitRow = CosBitRowMap[txw_idx][txh_idx];
this.TransformFunctionTypeColumn = TransformFunctionTypeMap[txh_idx][(int)this.TransformTypeColumn];
this.TransformFunctionTypeRow = TransformFunctionTypeMap[txw_idx][(int)this.TransformTypeRow];
this.CosBitColumn = CosBitColumnMap[transformWidthIndex][transformHeightIndex];
this.CosBitRow = CosBitRowMap[transformWidthIndex][transformHeightIndex];
this.TransformFunctionTypeColumn = TransformFunctionTypeMap[transformHeightIndex][(int)this.TransformTypeColumn];
this.TransformFunctionTypeRow = TransformFunctionTypeMap[transformWidthIndex][(int)this.TransformTypeRow];
this.StageNumberColumn = this.TransformFunctionTypeColumn != Av1TransformFunctionType.Invalid ? StageNumberList[(int)this.TransformFunctionTypeColumn] : -1;
this.StageNumberRow = this.TransformFunctionTypeRow != Av1TransformFunctionType.Invalid ? StageNumberList[(int)this.TransformFunctionTypeRow] : -1;
this.StageRangeColumn = new byte[12];
@ -147,40 +189,89 @@ internal class Av1Transform2dFlipConfiguration
this.NonScaleRange();
}
/// <summary>
/// Gets the fixed-point cosine precision used by the column transform.
/// </summary>
public int CosBitColumn { get; }
/// <summary>
/// Gets the fixed-point cosine precision used by the row transform.
/// </summary>
public int CosBitRow { get; }
/// <summary>
/// Gets the one-dimensional transform type applied down columns.
/// </summary>
public Av1TransformType1d TransformTypeColumn { get; }
/// <summary>
/// Gets the one-dimensional transform type applied across rows.
/// </summary>
public Av1TransformType1d TransformTypeRow { get; }
/// <summary>
/// Gets the concrete staged transform function applied down columns.
/// </summary>
public Av1TransformFunctionType TransformFunctionTypeColumn { get; }
/// <summary>
/// Gets the concrete staged transform function applied across rows.
/// </summary>
public Av1TransformFunctionType TransformFunctionTypeRow { get; }
/// <summary>
/// Gets the number of fixed-point stages in the column transform.
/// </summary>
public int StageNumberColumn { get; }
/// <summary>
/// Gets the number of fixed-point stages in the row transform.
/// </summary>
public int StageNumberRow { get; }
/// <summary>
/// Gets the transform-block dimensions.
/// </summary>
public Av1TransformSize TransformSize { get; }
/// <summary>
/// Gets the compound horizontal and vertical transform type.
/// </summary>
public Av1TransformType TransformType { get; }
/// <summary>
/// Gets a value indicating whether column input is traversed from bottom to top.
/// </summary>
public bool FlipUpsideDown { get; private set; }
/// <summary>
/// Gets a value indicating whether row output is written from right to left.
/// </summary>
public bool FlipLeftToRight { get; private set; }
/// <summary>
/// Gets the three fixed-point shifts applied by the two-dimensional transform pipeline.
/// </summary>
public Span<int> Shift => this.shift;
/// <summary>
/// Gets the allowed signed-bit range after each column-transform stage.
/// </summary>
public byte[] StageRangeColumn { get; }
/// <summary>
/// Gets the allowed signed-bit range after each row-transform stage.
/// </summary>
public byte[] StageRangeRow { get; }
/// <summary>
/// SVT: svt_av1_gen_fwd_stage_range
/// SVT: svt_av1_gen_inv_stage_range
/// Adds input bit depth and inter-stage shifts to the non-scaled stage ranges.
/// </summary>
/// <param name="bitDepth">The coded sample bit depth.</param>
/// <remarks>
/// Corresponds to <c>svt_av1_gen_fwd_stage_range</c> and <c>svt_av1_gen_inv_stage_range</c>
/// in the original WIP reference.
/// </remarks>
public void GenerateStageRange(int bitDepth)
{
// Take the shift from the larger dimension in the rectangular case.
@ -200,8 +291,10 @@ internal class Av1Transform2dFlipConfiguration
}
/// <summary>
/// SVT: is_txfm_allowed
/// Determines whether the transform type is permitted for the configured dimensions.
/// </summary>
/// <returns><see langword="true"/> when the transform combination is valid for the transform size.</returns>
/// <remarks>Corresponds to <c>is_txfm_allowed</c> in the original WIP reference.</remarks>
public bool IsAllowed()
{
Av1TransformType[] supportedTypes =
@ -249,14 +342,29 @@ internal class Av1Transform2dFlipConfiguration
return supportedTypes.Contains(this.TransformType);
}
/// <summary>
/// Replaces the three transform-pipeline shifts.
/// </summary>
/// <param name="shift0">The pre-column-transform shift.</param>
/// <param name="shift1">The shift between the column and row transforms.</param>
/// <param name="shift2">The post-row-transform shift.</param>
internal void SetShift(int shift0, int shift1, int shift2) => this.shift = [shift0, shift1, shift2];
/// <summary>
/// Overrides the axis traversal directions.
/// </summary>
/// <param name="upsideDown">Whether column input is traversed from bottom to top.</param>
/// <param name="leftToRight">Whether row output is written from right to left.</param>
internal void SetFlip(bool upsideDown, bool leftToRight)
{
this.FlipUpsideDown = upsideDown;
this.FlipLeftToRight = leftToRight;
}
/// <summary>
/// Derives the axis traversal directions encoded by a compound transform type.
/// </summary>
/// <param name="transformType">The compound transform type.</param>
private void SetFlip(Av1TransformType transformType)
{
switch (transformType)
@ -299,26 +407,27 @@ internal class Av1Transform2dFlipConfiguration
}
/// <summary>
/// SVT: set_fwd_txfm_non_scale_range
/// Initializes the per-stage signed-bit ranges before input depth and pipeline shifts are applied.
/// </summary>
/// <remarks>Corresponds to <c>set_fwd_txfm_non_scale_range</c> in the original WIP reference.</remarks>
private void NonScaleRange()
{
if (this.TransformFunctionTypeColumn != Av1TransformFunctionType.Invalid)
{
Span<int> range_mult2_col = RangeMulti2List[(int)this.TransformFunctionTypeColumn];
int stage_num_col = this.StageNumberColumn;
for (int i = 0; i < stage_num_col; ++i)
Span<int> columnRangeTimesTwo = RangeMulti2List[(int)this.TransformFunctionTypeColumn];
int columnStageCount = this.StageNumberColumn;
for (int i = 0; i < columnStageCount; ++i)
{
this.StageRangeColumn[i] = (byte)((range_mult2_col[i] + 1) >> 1);
this.StageRangeColumn[i] = (byte)((columnRangeTimesTwo[i] + 1) >> 1);
}
if (this.TransformFunctionTypeRow != Av1TransformFunctionType.Invalid)
{
int stage_num_row = this.StageNumberRow;
Span<int> range_mult2_row = RangeMulti2List[(int)this.TransformFunctionTypeRow];
for (int i = 0; i < stage_num_row; ++i)
int rowStageCount = this.StageNumberRow;
Span<int> rowRangeTimesTwo = RangeMulti2List[(int)this.TransformFunctionTypeRow];
for (int i = 0; i < rowStageCount; ++i)
{
this.StageRangeRow[i] = (byte)((range_mult2_col[this.StageNumberColumn - 1] + range_mult2_row[i] + 1) >> 1);
this.StageRangeRow[i] = (byte)((columnRangeTimesTwo[this.StageNumberColumn - 1] + rowRangeTimesTwo[i] + 1) >> 1);
}
}
}

3
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformClass.cs

@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Classifies AV1 transforms by the axes along which non-identity transforms operate.
/// </summary>
internal enum Av1TransformClass
{
Class2D = 0,

21
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformFunctionParameters.cs

@ -3,17 +3,38 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Carries the syntax and sample-storage parameters required to reconstruct one AV1 transform block.
/// </summary>
internal class Av1TransformFunctionParameters
{
/// <summary>
/// Gets or sets the compound transform type.
/// </summary>
public Av1TransformType TransformType { get; internal set; }
/// <summary>
/// Gets or sets the transform-block dimensions.
/// </summary>
public Av1TransformSize TransformSize { get; internal set; }
/// <summary>
/// Gets or sets the number of coefficient positions represented by the decoded coefficient buffer.
/// </summary>
public int EndOfBuffer { get; internal set; }
/// <summary>
/// Gets or sets a value indicating whether the coded segment uses the AV1 lossless transform rules.
/// </summary>
public bool IsLossless { get; internal set; }
/// <summary>
/// Gets or sets the decoded sample bit depth.
/// </summary>
public int BitDepth { get; internal set; }
/// <summary>
/// Gets or sets a value indicating whether reconstructed samples use the 16-bit storage pipeline.
/// </summary>
public bool Is16BitPipeline { get; internal set; }
}

3
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformFunctionType.cs

@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Identifies a concrete one-dimensional AV1 transform function and length.
/// </summary>
internal enum Av1TransformFunctionType
{
Dct4,

3
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformMode.cs

@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Identifies how transform-block sizes are selected within an AV1 frame.
/// </summary>
internal enum Av1TransformMode : byte
{
Only4x4 = 0,

3
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSetType.cs

@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Identifies the set of transform combinations allowed for an AV1 block.
/// </summary>
internal enum Av1TransformSetType
{
/// <summary>

3
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSize.cs

@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Identifies every square and rectangular transform-block size defined by AV1.
/// </summary>
internal enum Av1TransformSize : byte
{
Size4x4 = 0,

115
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformSizeExtensions.cs

@ -3,11 +3,20 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Provides dimensions, block mappings, scaling values, and subdivision rules for AV1 transform sizes.
/// </summary>
internal static class Av1TransformSizeExtensions
{
/// <summary>
/// The coefficient count for each transform-size enum value.
/// </summary>
private static readonly int[] Size2d = [
16, 64, 256, 1024, 4096, 32, 32, 128, 128, 512, 512, 2048, 2048, 64, 64, 256, 256, 1024, 1024];
/// <summary>
/// The transform size produced by one level of subdivision for each transform-size enum value.
/// </summary>
private static readonly Av1TransformSize[] SubTransformSize = [
Av1TransformSize.Size4x4, // TX_4X4
Av1TransformSize.Size4x4, // TX_8X8
@ -30,13 +39,19 @@ internal static class Av1TransformSizeExtensions
Av1TransformSize.Size32x16, // TX_64X16
];
// Transform block width in units.
/// <summary>
/// Transform widths in units of four samples.
/// </summary>
private static readonly int[] WideUnit = [1, 2, 4, 8, 16, 1, 2, 2, 4, 4, 8, 8, 16, 1, 4, 2, 8, 4, 16];
// Transform block height in unit
/// <summary>
/// Transform heights in units of four samples.
/// </summary>
private static readonly int[] HighUnit = [1, 2, 4, 8, 16, 2, 1, 4, 2, 8, 4, 16, 8, 4, 1, 8, 2, 16, 4];
// Transform size conversion into Block Size
/// <summary>
/// Maps each transform size to the block size with matching dimensions.
/// </summary>
private static readonly Av1BlockSize[] BlockSize = [
Av1BlockSize.Block4x4, // TX_4X4
Av1BlockSize.Block8x8, // TX_8X8
@ -59,6 +74,9 @@ internal static class Av1TransformSizeExtensions
Av1BlockSize.Block64x16, // TX_64X16
];
/// <summary>
/// Maps each transform size to the square transform based on its smaller dimension.
/// </summary>
private static readonly Av1TransformSize[] SquareMap = [
Av1TransformSize.Size4x4, // TX_4X4
Av1TransformSize.Size8x8, // TX_8X8
@ -81,6 +99,9 @@ internal static class Av1TransformSizeExtensions
Av1TransformSize.Size16x16, // TX_64X16
];
/// <summary>
/// Maps each transform size to the square transform based on its larger dimension.
/// </summary>
private static readonly Av1TransformSize[] SquareUpMap = [
Av1TransformSize.Size4x4, // TX_4X4
Av1TransformSize.Size8x8, // TX_8X8
@ -103,8 +124,9 @@ internal static class Av1TransformSizeExtensions
Av1TransformSize.Size64x64, // TX_64X16
];
// This is computed as:
// min(transform_width_log2, 5) + min(transform_height_log2, 5) - 4.
/// <summary>
/// Contains <c>min(log2(width), 5) + min(log2(height), 5) - 4</c> for each transform size.
/// </summary>
private static readonly int[] Log2Minus4 = [
0, // TX_4X4
2, // TX_8X8
@ -127,42 +149,106 @@ internal static class Av1TransformSizeExtensions
5, // TX_64X16
];
// Transform block width in log2
/// <summary>
/// Transform widths expressed as base-two logarithms of sample counts.
/// </summary>
private static readonly int[] BlockWidthLog2 = [
2, 3, 4, 5, 6, 2, 3, 3, 4, 4, 5, 5, 6, 2, 4, 3, 5, 4, 6,
];
// Transform block height in log2
/// <summary>
/// Transform heights expressed as base-two logarithms of sample counts.
/// </summary>
private static readonly int[] BlockHeightLog2 = [
2, 3, 4, 5, 6, 3, 2, 4, 3, 5, 4, 6, 5, 4, 2, 5, 3, 6, 4,
];
/// <summary>
/// Gets the number of coefficient positions in a transform block.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The transform width multiplied by its height.</returns>
public static int GetSize2d(this Av1TransformSize size) => Size2d[(int)size];
/// <summary>
/// Gets the inverse-quantization scale category for a transform size.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>Zero for up to 256 coefficients, one for up to 1024, or two for larger transforms.</returns>
public static int GetScale(this Av1TransformSize size)
{
int pels = Size2d[(int)size];
return (pels > 1024) ? 2 : (pels > 256) ? 1 : 0;
}
/// <summary>
/// Gets the transform width in samples.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The transform width in samples.</returns>
public static int GetWidth(this Av1TransformSize size) => WideUnit[(int)size] << 2;
/// <summary>
/// Gets the transform height in samples.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The transform height in samples.</returns>
public static int GetHeight(this Av1TransformSize size) => HighUnit[(int)size] << 2;
/// <summary>
/// Gets the transform width in units of four samples.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The number of four-sample columns.</returns>
public static int Get4x4WideCount(this Av1TransformSize size) => WideUnit[(int)size];
/// <summary>
/// Gets the transform height in units of four samples.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The number of four-sample rows.</returns>
public static int Get4x4HighCount(this Av1TransformSize size) => HighUnit[(int)size];
/// <summary>
/// Gets the next smaller transform size used when a transform block is subdivided.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The transform's subdivision size.</returns>
public static Av1TransformSize GetSubSize(this Av1TransformSize size) => SubTransformSize[(int)size];
/// <summary>
/// Gets the square transform based on the smaller dimension of a rectangular transform.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The corresponding square transform size.</returns>
public static Av1TransformSize GetSquareSize(this Av1TransformSize size) => SquareMap[(int)size];
/// <summary>
/// Gets the square transform based on the larger dimension of a rectangular transform.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The corresponding enclosing square transform size.</returns>
public static Av1TransformSize GetSquareUpSize(this Av1TransformSize size) => SquareUpMap[(int)size];
/// <summary>
/// Gets the block size having the same dimensions as a transform size.
/// </summary>
/// <param name="transformSize">The transform size.</param>
/// <returns>The dimensionally equivalent block size.</returns>
public static Av1BlockSize ToBlockSize(this Av1TransformSize transformSize) => BlockSize[(int)transformSize];
/// <summary>
/// Gets the capped sum of the transform-dimension logarithms minus four.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The context value used by AV1 transform syntax.</returns>
public static int GetLog2Minus4(this Av1TransformSize size) => Log2Minus4[(int)size];
/// <summary>
/// Gets the transform size used by coefficient and quantization tables that cap dimensions at 32 samples.
/// </summary>
/// <param name="size">The signaled transform size.</param>
/// <returns>The adjusted transform size.</returns>
public static Av1TransformSize GetAdjusted(this Av1TransformSize size) => size switch
{
Av1TransformSize.Size64x64 or Av1TransformSize.Size64x32 or Av1TransformSize.Size32x64 => Av1TransformSize.Size32x32,
@ -171,10 +257,25 @@ internal static class Av1TransformSizeExtensions
_ => size
};
/// <summary>
/// Gets the base-two logarithm of the transform width in samples.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The base-two width logarithm.</returns>
public static int GetBlockWidthLog2(this Av1TransformSize size) => BlockWidthLog2[(int)size];
/// <summary>
/// Gets the base-two logarithm of the transform height in samples.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>The base-two height logarithm.</returns>
public static int GetBlockHeightLog2(this Av1TransformSize size) => BlockHeightLog2[(int)size];
/// <summary>
/// Gets the signed base-two ratio between transform width and height.
/// </summary>
/// <param name="size">The transform size.</param>
/// <returns>Zero for square transforms, positive when wider, or negative when taller.</returns>
public static int GetRectangleLogRatio(this Av1TransformSize size)
{
int col = GetWidth(size);

3
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformType.cs

@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Identifies the horizontal and vertical transform combination signaled for an AV1 transform block.
/// </summary>
internal enum Av1TransformType : byte
{
/// <summary>

3
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformType1d.cs

@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Identifies the one-dimensional transform applied along one axis of an AV1 transform block.
/// </summary>
internal enum Av1TransformType1d
{
Dct,

20
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformTypeExtensions.cs

@ -3,8 +3,14 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Provides AV1 transform-class and transform-set lookups for compound transform types.
/// </summary>
internal static class Av1TransformTypeExtensions
{
/// <summary>
/// Maps each compound transform type to its entropy-context transform class.
/// </summary>
private static readonly Av1TransformClass[] Type2Class = [
Av1TransformClass.Class2D, // DCT_DCT
Av1TransformClass.Class2D, // ADST_DCT
@ -24,6 +30,9 @@ internal static class Av1TransformTypeExtensions
Av1TransformClass.ClassHorizontal, // H_FLIPADST
];
/// <summary>
/// Indicates which compound transform types are enabled by each transform-set type.
/// </summary>
private static readonly bool[][] ExtendedTransformUsed = [
[true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false],
[true, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false],
@ -33,8 +42,19 @@ internal static class Av1TransformTypeExtensions
[true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true],
];
/// <summary>
/// Gets the entropy-context class of a compound transform type.
/// </summary>
/// <param name="transformType">The compound transform type.</param>
/// <returns>The two-dimensional, horizontal, or vertical transform class.</returns>
public static Av1TransformClass ToClass(this Av1TransformType transformType) => Type2Class[(int)transformType];
/// <summary>
/// Determines whether a compound transform type belongs to an allowed transform set.
/// </summary>
/// <param name="transformType">The compound transform type.</param>
/// <param name="setType">The allowed transform set.</param>
/// <returns><see langword="true"/> when the transform is enabled by the set.</returns>
public static bool IsExtendedSetUsed(this Av1TransformType transformType, Av1TransformSetType setType)
=> ExtendedTransformUsed[(int)setType][(int)transformType];
}

10
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst16Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the 16-point AV1 forward asymmetric discrete sine transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Adst16Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 16, nameof(input));
@ -14,6 +18,12 @@ internal class Av1Adst16Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0], cosBit);
}
/// <summary>
/// Applies the staged 16-point fixed-point forward ADST.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
private static void TransformScalar(ref int input, ref int output, int cosBit)
{
Span<int> temp0 = stackalloc int[16];

10
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst32Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the 32-point AV1 forward asymmetric discrete sine transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Adst32Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 32, nameof(input));
@ -14,6 +18,12 @@ internal class Av1Adst32Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0], cosBit);
}
/// <summary>
/// Applies the staged 32-point fixed-point forward ADST.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="outputRef">A reference to the first output coefficient.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
private static void TransformScalar(ref int input, ref int outputRef, int cosBit)
{
Span<int> temp0 = stackalloc int[32];

10
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst4Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the four-point AV1 forward asymmetric discrete sine transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Adst4Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 4, nameof(input));
@ -14,6 +18,12 @@ internal class Av1Adst4Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0], cosBit);
}
/// <summary>
/// Applies the staged four-point fixed-point forward ADST.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
private static void TransformScalar(ref int input, ref int output, int cosBit)
{
Span<int> sinpi = Av1SinusConstants.SinusPi(cosBit);

10
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst8Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the eight-point AV1 forward asymmetric discrete sine transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Adst8Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 8, nameof(input));
@ -14,6 +18,12 @@ internal class Av1Adst8Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0], cosBit);
}
/// <summary>
/// Applies the staged eight-point fixed-point forward ADST.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
private static void TransformScalar(ref int input, ref int output, int cosBit)
{
Span<int> temp0 = stackalloc int[8];

10
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct16Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the 16-point AV1 forward discrete cosine transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Dct16Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 16, nameof(input));
@ -14,6 +18,12 @@ internal class Av1Dct16Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0], cosBit);
}
/// <summary>
/// Applies the staged 16-point fixed-point forward DCT.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
private static void TransformScalar(ref int input, ref int output, int cosBit)
{
Span<int> temp0 = stackalloc int[16];

10
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct32Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the 32-point AV1 forward discrete cosine transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Dct32Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 32, nameof(input));
@ -14,6 +18,12 @@ internal class Av1Dct32Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0], cosBit);
}
/// <summary>
/// Applies the staged 32-point fixed-point forward DCT.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
private static void TransformScalar(ref int input, ref int output, int cosBit)
{
Span<int> temp0 = stackalloc int[32];

19
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct4Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the four-point AV1 forward discrete cosine transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Dct4Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 4, nameof(input));
@ -14,6 +18,12 @@ internal class Av1Dct4Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0], cosBit);
}
/// <summary>
/// Applies the staged four-point fixed-point forward DCT.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
private static void TransformScalar(ref int input, ref int output, int cosBit)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
@ -49,6 +59,15 @@ internal class Av1Dct4Forward1dTransformer : IAv1Transformer1d
output3 = step3;
}
/// <summary>
/// Applies one rounded two-input fixed-point butterfly output.
/// </summary>
/// <param name="w0">The first fixed-point weight.</param>
/// <param name="in0">The first input value.</param>
/// <param name="w1">The second fixed-point weight.</param>
/// <param name="in1">The second input value.</param>
/// <param name="bit">The number of fractional bits removed after multiplication.</param>
/// <returns>The rounded butterfly output.</returns>
internal static int HalfButterfly(int w0, int in0, int w1, int in1, int bit)
{
long result64 = (long)(w0 * in0) + (w1 * in1);

10
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct64Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the 64-point AV1 forward discrete cosine transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Dct64Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 64, nameof(input));
@ -14,6 +18,12 @@ internal class Av1Dct64Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0], cosBit);
}
/// <summary>
/// Applies the staged 64-point fixed-point forward DCT.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
private static void TransformScalar(ref int input, ref int output, int cosBit)
{
Span<int> temp0 = stackalloc int[64];

10
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct8Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the eight-point AV1 forward discrete cosine transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Dct8Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 8, nameof(input));
@ -14,6 +18,12 @@ internal class Av1Dct8Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0], cosBit);
}
/// <summary>
/// Applies the staged eight-point fixed-point forward DCT.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
private static void TransformScalar(ref int input, ref int output, int cosBit)
{
Span<int> temp0 = stackalloc int[8];

28
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1DctDct4Forward2dTransformer.cs

@ -6,12 +6,33 @@ using System.Runtime.Intrinsics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the AV1 four-by-four forward two-dimensional DCT-DCT transform.
/// </summary>
internal class Av1DctDct4Forward2dTransformer : Av1Forward2dTransformerBase
{
/// <summary>
/// The fixed transform configuration for a four-by-four DCT-DCT block.
/// </summary>
private readonly Av1Transform2dFlipConfiguration config = new(Av1TransformType.DctDct, Av1TransformSize.Size4x4);
/// <summary>
/// The four-point one-dimensional DCT reused for both axes.
/// </summary>
private readonly Av1Dct4Forward1dTransformer transformer = new();
/// <summary>
/// The transposed intermediate coefficient plane used by the scalar two-dimensional pipeline.
/// </summary>
private readonly int[] temp = new int[Av1Constants.MaxTransformSize * Av1Constants.MaxTransformSize];
/// <summary>
/// Applies the four-by-four DCT-DCT transform to a residual block.
/// </summary>
/// <param name="input">The spatial residual samples.</param>
/// <param name="output">The destination transform coefficients.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
/// <param name="columnNumber">The number of input values between adjacent rows.</param>
public void Transform(Span<short> input, Span<int> output, int cosBit, int columnNumber)
{
/*if (Vector256.IsHardwareAccelerated)
@ -27,8 +48,13 @@ internal class Av1DctDct4Forward2dTransformer : Av1Forward2dTransformerBase
}
/// <summary>
/// SVT: fdct4x4_sse4_1
/// Applies the vectorized four-by-four forward DCT-DCT kernel.
/// </summary>
/// <param name="input">A reference to the first vector of residual samples.</param>
/// <param name="output">A reference to the first vector of transform coefficients.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
/// <param name="columnNumber">The number of vectors between adjacent input rows.</param>
/// <remarks>Corresponds to <c>fdct4x4_sse4_1</c> in the original WIP reference.</remarks>
private static void TransformVector(ref Vector128<int> input, ref Vector128<int> output, int cosBit, int columnNumber)
{
// We only use stage-2 bit;

35
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Forward2dTransformerBase.cs

@ -5,14 +5,35 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Provides the separable row-and-column pipeline shared by AV1 forward two-dimensional transforms.
/// </summary>
internal abstract class Av1Forward2dTransformerBase
{
/// <summary>
/// The fixed-point representation of <c>sqrt(2)</c> at <see cref="NewSqrt2BitCount"/> fractional bits.
/// </summary>
internal const int NewSqrt2 = 5793;
/// <summary>
/// The number of fractional bits used by <see cref="NewSqrt2"/>.
/// </summary>
internal const int NewSqrt2BitCount = 12;
/// <summary>
/// SVT: av1_tranform_two_d_core_c
/// Applies the separable column and row stages, including normative flips, shifts, and rectangular scaling.
/// </summary>
/// <typeparam name="TColumn">The column-transform implementation type.</typeparam>
/// <typeparam name="TRow">The row-transform implementation type.</typeparam>
/// <param name="transformFunctionColumn">The column-transform implementation.</param>
/// <param name="transformFunctionRow">The row-transform implementation.</param>
/// <param name="input">The spatial residual samples.</param>
/// <param name="inputStride">The number of input samples between rows.</param>
/// <param name="output">The destination transform coefficients and temporary axis buffers.</param>
/// <param name="config">The per-axis transform, flip, shift, and range configuration.</param>
/// <param name="buf">The transposed intermediate coefficient plane.</param>
/// <param name="bitDepth">The source sample bit depth.</param>
/// <remarks>Corresponds to <c>av1_tranform_two_d_core_c</c> in the original WIP reference.</remarks>
protected static void Transform2dCore<TColumn, TRow>(TColumn transformFunctionColumn, TRow transformFunctionRow, Span<short> input, uint inputStride, Span<int> output, Av1Transform2dFlipConfiguration config, Span<int> buf, int bitDepth)
where TColumn : IAv1Transformer1d
where TRow : IAv1Transformer1d
@ -119,6 +140,12 @@ internal abstract class Av1Forward2dTransformerBase
}
}
/// <summary>
/// Applies a signed fixed-point shift to a contiguous transform-stage vector.
/// </summary>
/// <param name="arr">A reference to the first transform-stage value.</param>
/// <param name="size">The number of values to update.</param>
/// <param name="bit">A positive rounded-right shift or a negative exact-left shift.</param>
private static void RoundShiftArray(ref int arr, int size, int bit)
{
if (bit == 0)
@ -148,8 +175,12 @@ internal abstract class Av1Forward2dTransformerBase
}
/// <summary>
/// SVT: get_rect_tx_log_ratio
/// Gets the signed base-two ratio between transform columns and rows.
/// </summary>
/// <param name="col">The transform width.</param>
/// <param name="row">The transform height.</param>
/// <returns>Zero for square transforms, positive when wider, or negative when taller.</returns>
/// <remarks>Corresponds to <c>get_rect_tx_log_ratio</c> in the original WIP reference.</remarks>
public static int GetRectangularRatio(int col, int row)
{
if (col == row)

9
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity16Forward1dTransformer.cs

@ -5,10 +5,14 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the 16-point AV1 forward identity transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Identity16Forward1dTransformer : IAv1Transformer1d
{
private const int TwiceNewSqrt2 = 2 * 5793;
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 16, nameof(input));
@ -16,6 +20,11 @@ internal class Av1Identity16Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0]);
}
/// <summary>
/// Scales 16 residual values according to the AV1 forward identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
private static void TransformScalar(ref int input, ref int output)
{
output = Av1Math.RoundShift((long)input * TwiceNewSqrt2, Av1Forward2dTransformerBase.NewSqrt2BitCount);

9
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity32Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the 32-point AV1 forward identity transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Identity32Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 32, nameof(input));
@ -19,6 +23,11 @@ internal class Av1Identity32Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref Unsafe.Add(ref inputRef, 24), ref Unsafe.Add(ref outputRef, 24));
}
/// <summary>
/// Scales 32 residual values according to the AV1 forward identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
private static void TransformScalar(ref int input, ref int output)
{
output = input << 2;

9
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity4Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the four-point AV1 forward identity transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Identity4Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 4, nameof(input));
@ -14,6 +18,11 @@ internal class Av1Identity4Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0]);
}
/// <summary>
/// Scales four residual values according to the AV1 forward identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
private static void TransformScalar(ref int input, ref int output)
{
output = Av1Math.RoundShift((long)input * Av1Forward2dTransformerBase.NewSqrt2, Av1Forward2dTransformerBase.NewSqrt2BitCount);

9
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity64Forward1dTransformer.cs

@ -5,10 +5,14 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the 64-point AV1 forward identity transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Identity64Forward1dTransformer : IAv1Transformer1d
{
private const int QuadNewSqrt2 = 4 * 5793;
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 64, nameof(input));
@ -21,6 +25,11 @@ internal class Av1Identity64Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref Unsafe.Add(ref inputRef, 48), ref Unsafe.Add(ref outputRef, 48));
}
/// <summary>
/// Scales 64 residual values according to the AV1 forward identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
private static void TransformScalar(ref int input, ref int output)
{
output = Av1Math.RoundShift((long)input * QuadNewSqrt2, Av1Forward2dTransformerBase.NewSqrt2BitCount);

9
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity8Forward1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward;
/// <summary>
/// Applies the eight-point AV1 forward identity transform to a one-dimensional residual vector.
/// </summary>
internal class Av1Identity8Forward1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 8, nameof(input));
@ -14,6 +18,11 @@ internal class Av1Identity8Forward1dTransformer : IAv1Transformer1d
TransformScalar(ref input[0], ref output[0]);
}
/// <summary>
/// Scales eight residual values according to the AV1 forward identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input value.</param>
/// <param name="output">A reference to the first output coefficient.</param>
private static void TransformScalar(ref int input, ref int output)
{
output = input << 1;

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

@ -4,7 +4,7 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
/// <summary>
/// Implementation of a specific forward 1-dimensional transform function.
/// Defines a one-dimensional AV1 forward or inverse transform function.
/// </summary>
internal interface IAv1Transformer1d
{

11
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst16Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the 16-point AV1 inverse asymmetric discrete sine transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Adst16Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 16, nameof(input));
@ -15,8 +19,13 @@ internal class Av1Adst16Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_iadst16_new
/// Applies the staged 16-point fixed-point inverse ADST.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <param name="cos_bit">The cosine-table fixed-point precision.</param>
/// <param name="stage_range">The signed-bit range permitted after each transform stage.</param>
/// <remarks>Corresponds to <c>svt_av1_iadst16_new</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output, int cos_bit, Span<byte> stage_range)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cos_bit);

11
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst32Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the 32-point AV1 inverse asymmetric discrete sine transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Adst32Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 32, nameof(input));
@ -15,8 +19,13 @@ internal class Av1Adst32Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_iadst32_new
/// Applies the staged 32-point fixed-point inverse ADST.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
/// <param name="stageRange">The signed-bit range permitted after each transform stage.</param>
/// <remarks>Corresponds to <c>svt_av1_iadst32_new</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit);

11
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst4Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the four-point AV1 inverse asymmetric discrete sine transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Adst4Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 4, nameof(input));
@ -15,8 +19,13 @@ internal class Av1Adst4Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_iadst4_new
/// Applies the staged four-point fixed-point inverse ADST.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <param name="cosBit">The sine-table fixed-point precision.</param>
/// <param name="stageRange">The signed-bit range permitted after each transform stage.</param>
/// <remarks>Corresponds to <c>svt_av1_iadst4_new</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
int bit = cosBit;

11
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Adst8Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the eight-point AV1 inverse asymmetric discrete sine transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Adst8Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 8, nameof(input));
@ -15,8 +19,13 @@ internal class Av1Adst8Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_iadst8_new
/// Applies the staged eight-point fixed-point inverse ADST.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
/// <param name="stageRange">The signed-bit range permitted after each transform stage.</param>
/// <remarks>Corresponds to <c>svt_av1_iadst8_new</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit);

26
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct16Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the 16-point AV1 inverse discrete cosine transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Dct16Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 16, nameof(input));
@ -15,8 +19,13 @@ internal class Av1Dct16Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_idct16_new
/// Applies the staged 16-point fixed-point inverse DCT.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
/// <param name="stageRange">The signed-bit range permitted after each transform stage.</param>
/// <remarks>Corresponds to <c>svt_av1_idct16_new</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
@ -177,6 +186,12 @@ internal class Av1Dct16Inverse1dTransformer : IAv1Transformer1d
Unsafe.Add(ref output, 15) = ClampValue(temp1[0] - temp1[15], range);
}
/// <summary>
/// Clamps one transform-stage value to a signed range of the specified bit width.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="bit">The signed range width in bits.</param>
/// <returns>The clamped value.</returns>
internal static int ClampValue(int value, byte bit)
{
if (bit <= 0)
@ -189,6 +204,15 @@ internal class Av1Dct16Inverse1dTransformer : IAv1Transformer1d
return (int)Av1Math.Clamp(value, min_value, max_value);
}
/// <summary>
/// Applies one rounded two-input fixed-point butterfly output.
/// </summary>
/// <param name="w0">The first fixed-point weight.</param>
/// <param name="in0">The first input value.</param>
/// <param name="w1">The second fixed-point weight.</param>
/// <param name="in1">The second input value.</param>
/// <param name="bit">The number of fractional bits removed after multiplication.</param>
/// <returns>The rounded butterfly output.</returns>
internal static int HalfButterfly(int w0, int in0, int w1, int in1, int bit)
{
long result64 = (long)(w0 * in0) + (w1 * in1);

26
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct32Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the 32-point AV1 inverse discrete cosine transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Dct32Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 32, nameof(input));
@ -15,8 +19,13 @@ internal class Av1Dct32Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_idct32_new
/// Applies the staged 32-point fixed-point inverse DCT.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
/// <param name="stageRange">The signed-bit range permitted after each transform stage.</param>
/// <remarks>Corresponds to <c>svt_av1_idct32_new</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
@ -365,6 +374,12 @@ internal class Av1Dct32Inverse1dTransformer : IAv1Transformer1d
Unsafe.Add(ref output, 31) = ClampValue(temp0[0] - temp0[31], range);
}
/// <summary>
/// Clamps one transform-stage value to a signed range of the specified bit width.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="bit">The signed range width in bits.</param>
/// <returns>The clamped value.</returns>
internal static int ClampValue(int value, byte bit)
{
if (bit <= 0)
@ -377,6 +392,15 @@ internal class Av1Dct32Inverse1dTransformer : IAv1Transformer1d
return (int)Av1Math.Clamp(value, min_value, max_value);
}
/// <summary>
/// Applies one rounded two-input fixed-point butterfly output.
/// </summary>
/// <param name="w0">The first fixed-point weight.</param>
/// <param name="in0">The first input value.</param>
/// <param name="w1">The second fixed-point weight.</param>
/// <param name="in1">The second input value.</param>
/// <param name="bit">The number of fractional bits removed after multiplication.</param>
/// <returns>The rounded butterfly output.</returns>
internal static int HalfButterfly(int w0, int in0, int w1, int in1, int bit)
{
long result64 = (long)(w0 * in0) + (w1 * in1);

46
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct4Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the four-point AV1 inverse discrete cosine transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Dct4Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 4, nameof(input));
@ -15,8 +19,13 @@ internal class Av1Dct4Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_idct4_new
/// Applies the staged four-point fixed-point inverse DCT.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
/// <param name="stageRange">The signed-bit range permitted after each transform stage.</param>
/// <remarks>Corresponds to <c>svt_av1_idct4_new</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
@ -53,6 +62,12 @@ internal class Av1Dct4Inverse1dTransformer : IAv1Transformer1d
ClampBuffer4(ref output, stageRange[stage]);
}
/// <summary>
/// Clamps one transform-stage value to a signed range of the specified bit width.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="bit">The signed range width in bits.</param>
/// <returns>The clamped value.</returns>
internal static int ClampValue(int value, byte bit)
{
if (bit <= 0)
@ -65,6 +80,11 @@ internal class Av1Dct4Inverse1dTransformer : IAv1Transformer1d
return (int)Av1Math.Clamp(value, min_value, max_value);
}
/// <summary>
/// Clamps four contiguous transform-stage values to a signed range.
/// </summary>
/// <param name="buffer">A reference to the first value.</param>
/// <param name="bit">The signed range width in bits.</param>
internal static void ClampBuffer4(ref int buffer, byte bit)
{
if (bit <= 0)
@ -81,6 +101,11 @@ internal class Av1Dct4Inverse1dTransformer : IAv1Transformer1d
Unsafe.Add(ref buffer, 3) = (int)Av1Math.Clamp(Unsafe.Add(ref buffer, 3), min_value, max_value);
}
/// <summary>
/// Clamps eight contiguous transform-stage values to a signed range.
/// </summary>
/// <param name="buffer">A reference to the first value.</param>
/// <param name="bit">The signed range width in bits.</param>
internal static void ClampBuffer8(ref int buffer, byte bit)
{
if (bit <= 0)
@ -101,6 +126,11 @@ internal class Av1Dct4Inverse1dTransformer : IAv1Transformer1d
Unsafe.Add(ref buffer, 7) = (int)Av1Math.Clamp(Unsafe.Add(ref buffer, 7), min_value, max_value);
}
/// <summary>
/// Clamps 16 contiguous transform-stage values to a signed range.
/// </summary>
/// <param name="buffer">A reference to the first value.</param>
/// <param name="bit">The signed range width in bits.</param>
internal static void ClampBuffer16(ref int buffer, byte bit)
{
if (bit <= 0)
@ -129,6 +159,11 @@ internal class Av1Dct4Inverse1dTransformer : IAv1Transformer1d
Unsafe.Add(ref buffer, 15) = (int)Av1Math.Clamp(Unsafe.Add(ref buffer, 15), min_value, max_value);
}
/// <summary>
/// Clamps 32 contiguous transform-stage values to a signed range.
/// </summary>
/// <param name="buffer">A reference to the first value.</param>
/// <param name="bit">The signed range width in bits.</param>
internal static void ClampBuffer32(ref int buffer, byte bit)
{
if (bit <= 0)
@ -173,6 +208,15 @@ internal class Av1Dct4Inverse1dTransformer : IAv1Transformer1d
Unsafe.Add(ref buffer, 31) = (int)Av1Math.Clamp(Unsafe.Add(ref buffer, 31), min_value, max_value);
}
/// <summary>
/// Applies one rounded two-input fixed-point butterfly output.
/// </summary>
/// <param name="w0">The first fixed-point weight.</param>
/// <param name="in0">The first input value.</param>
/// <param name="w1">The second fixed-point weight.</param>
/// <param name="in1">The second input value.</param>
/// <param name="bit">The number of fractional bits removed after multiplication.</param>
/// <returns>The rounded butterfly output.</returns>
internal static int HalfButterfly(int w0, int in0, int w1, int in1, int bit)
{
long result64 = (long)(w0 * in0) + (w1 * in1);

11
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct64Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the 64-point AV1 inverse discrete cosine transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Dct64Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 64, nameof(input));
@ -15,8 +19,13 @@ internal class Av1Dct64Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_idct64_new
/// Applies the staged 64-point fixed-point inverse DCT.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
/// <param name="stageRange">The signed-bit range permitted after each transform stage.</param>
/// <remarks>Corresponds to <c>svt_av1_idct64_new</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit);

26
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Dct8Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the eight-point AV1 inverse discrete cosine transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Dct8Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 8, nameof(input));
@ -15,8 +19,13 @@ internal class Av1Dct8Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_idct8_new
/// Applies the staged eight-point fixed-point inverse DCT.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <param name="cosBit">The cosine-table fixed-point precision.</param>
/// <param name="stageRange">The signed-bit range permitted after each transform stage.</param>
/// <remarks>Corresponds to <c>svt_av1_idct8_new</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange)
{
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit);
@ -92,6 +101,12 @@ internal class Av1Dct8Inverse1dTransformer : IAv1Transformer1d
Unsafe.Add(ref output, 7) = ClampValue(temp1[0] - temp1[7], range);
}
/// <summary>
/// Clamps one transform-stage value to a signed range of the specified bit width.
/// </summary>
/// <param name="value">The value to clamp.</param>
/// <param name="bit">The signed range width in bits.</param>
/// <returns>The clamped value.</returns>
internal static int ClampValue(int value, byte bit)
{
if (bit <= 0)
@ -104,6 +119,15 @@ internal class Av1Dct8Inverse1dTransformer : IAv1Transformer1d
return (int)Av1Math.Clamp(value, min_value, max_value);
}
/// <summary>
/// Applies one rounded two-input fixed-point butterfly output.
/// </summary>
/// <param name="w0">The first fixed-point weight.</param>
/// <param name="in0">The first input value.</param>
/// <param name="w1">The second fixed-point weight.</param>
/// <param name="in1">The second input value.</param>
/// <param name="bit">The number of fractional bits removed after multiplication.</param>
/// <returns>The rounded butterfly output.</returns>
internal static int HalfButterfly(int w0, int in0, int w1, int in1, int bit)
{
long result64 = (long)(w0 * in0) + (w1 * in1);

9
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity16Inverse1dTransformer.cs

@ -5,10 +5,14 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the 16-point AV1 inverse identity transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Identity16Inverse1dTransformer : IAv1Transformer1d
{
private const long Sqrt2Times2 = Av1Identity4Inverse1dTransformer.Sqrt2 >> 1;
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 16, nameof(input));
@ -17,8 +21,11 @@ internal class Av1Identity16Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_iidentity16_c
/// Scales 16 coefficients according to the AV1 inverse identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <remarks>Corresponds to <c>svt_av1_iidentity16_c</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output)
{
// Normal input should fit into 32-bit. Cast to 64-bit here to avoid

9
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity32Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the 32-point AV1 inverse identity transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Identity32Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 32, nameof(input));
@ -20,8 +24,11 @@ internal class Av1Identity32Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_iidentity32_c
/// Scales 32 coefficients according to the AV1 inverse identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <remarks>Corresponds to <c>svt_av1_iidentity32_c</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output)
{
output = input << 2;

9
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity4Inverse1dTransformer.cs

@ -5,6 +5,9 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the four-point AV1 inverse identity transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Identity4Inverse1dTransformer : IAv1Transformer1d
{
internal const int Sqrt2Bits = 12;
@ -12,6 +15,7 @@ internal class Av1Identity4Inverse1dTransformer : IAv1Transformer1d
// 2^12 * sqrt(2)
internal const long Sqrt2 = 5793;
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 4, nameof(input));
@ -20,8 +24,11 @@ internal class Av1Identity4Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_iidentity4_c
/// Scales four coefficients according to the AV1 inverse identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <remarks>Corresponds to <c>svt_av1_iidentity4_c</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output)
{
// Normal input should fit into 32-bit. Cast to 64-bit here to avoid

9
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity64Inverse1dTransformer.cs

@ -5,10 +5,14 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the 64-point AV1 inverse identity transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Identity64Inverse1dTransformer : IAv1Transformer1d
{
private const long Sqrt2Times4 = Av1Identity4Inverse1dTransformer.Sqrt2 >> 2;
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 64, nameof(input));
@ -26,8 +30,11 @@ internal class Av1Identity64Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_iidentity64_c
/// Scales 64 coefficients according to the AV1 inverse identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <remarks>Corresponds to <c>svt_av1_iidentity64_c</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output)
{
// Normal input should fit into 32-bit. Cast to 64-bit here to avoid

9
src/ImageSharp/Formats/Heif/Av1/Transform/Inverse/Av1Identity8Inverse1dTransformer.cs

@ -5,8 +5,12 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse;
/// <summary>
/// Applies the eight-point AV1 inverse identity transform to a one-dimensional coefficient vector.
/// </summary>
internal class Av1Identity8Inverse1dTransformer : IAv1Transformer1d
{
/// <inheritdoc/>
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange)
{
Guard.MustBeSizedAtLeast(input, 8, nameof(input));
@ -15,8 +19,11 @@ internal class Av1Identity8Inverse1dTransformer : IAv1Transformer1d
}
/// <summary>
/// SVT: svt_av1_iidentity8_c
/// Scales eight coefficients according to the AV1 inverse identity-transform definition.
/// </summary>
/// <param name="input">A reference to the first input coefficient.</param>
/// <param name="output">A reference to the first output value.</param>
/// <remarks>Corresponds to <c>svt_av1_iidentity8_c</c> in the original WIP reference.</remarks>
private static void TransformScalar(ref int input, ref int output)
{
output = input << 1;

Loading…
Cancel
Save