diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
index 3f0473150..7bf346dc6 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1BlockModeInfo.cs
@@ -5,10 +5,22 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores block-size, prediction-mode, transform, and palette decisions shared by AV1 block processing.
+///
internal class Av1BlockModeInfo
{
+ ///
+ /// Stores the palette size for luma and for the shared chroma mode.
+ ///
private int[] paletteSize;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The number of color planes in the decoded frame.
+ /// The decoded block size.
+ /// The block origin relative to its superblock in 4x4 mode-information units.
public Av1BlockModeInfo(int numPlanes, Av1BlockSize blockSize, Point positionInSuperblock)
{
this.BlockSize = blockSize;
@@ -20,6 +32,9 @@ internal class Av1BlockModeInfo
this.TransformUnitsCount = new int[numPlanes - 1];
}
+ ///
+ /// Gets the decoded block size.
+ ///
public Av1BlockSize BlockSize { get; }
///
@@ -27,12 +42,24 @@ internal class Av1BlockModeInfo
///
public Av1PredictionMode YMode { get; set; }
+ ///
+ /// Gets or sets a value indicating whether residual coefficients are omitted for the block.
+ ///
public bool Skip { get; set; }
+ ///
+ /// Gets or sets the partition type that produced the block.
+ ///
public Av1PartitionType PartitionType { get; set; }
+ ///
+ /// Gets or sets a value indicating whether compound skip mode is selected.
+ ///
public bool SkipMode { get; set; }
+ ///
+ /// Gets or sets the segmentation identifier assigned to the block.
+ ///
public int SegmentId { get; set; }
///
@@ -40,31 +67,64 @@ internal class Av1BlockModeInfo
///
public Av1PredictionMode UvMode { get; set; }
+ ///
+ /// Gets or sets a value indicating whether intra block copy is selected.
+ ///
public bool UseUltraBlockCopy { get; set; }
+ ///
+ /// Gets or sets the packed chroma-from-luma alpha magnitude indices.
+ ///
public int ChromaFromLumaAlphaIndex { get; set; }
+ ///
+ /// Gets or sets the joint chroma-from-luma alpha sign value.
+ ///
public int ChromaFromLumaAlphaSign { get; set; }
+ ///
+ /// Gets or sets the directional prediction angle adjustments for the chroma planes.
+ ///
public int[] AngleDelta { get; set; }
///
- /// Gets the position relative to the Superblock, counted in mode info (4x4 pixels).
+ /// Gets the position relative to the superblock in 4x4 mode-information units.
///
public Point PositionInSuperblock { get; }
+ ///
+ /// Gets or sets the filter-intra syntax for the block.
+ ///
public Av1IntraFilterModeInfo FilterIntraModeInfo { get; internal set; }
///
- /// Gets the index of the first of this Mode Info in the .
+ /// Gets the plane-relative index of the first for this block.
///
public int[] FirstTransformLocation { get; }
+ ///
+ /// Gets or sets the number of transform units for luma and for each chroma plane.
+ ///
public int[] TransformUnitsCount { get; internal set; }
+ ///
+ /// Gets the palette size for the specified color plane.
+ ///
+ /// The color plane.
+ /// The palette size for the plane.
public int GetPaletteSize(Av1Plane plane) => this.paletteSize[Math.Min(1, (int)plane)];
+ ///
+ /// Gets the palette size for the specified plane class.
+ ///
+ /// The luma or chroma plane class.
+ /// The palette size for the plane class.
public int GetPaletteSize(Av1PlaneType planeType) => this.paletteSize[(int)planeType];
+ ///
+ /// Sets the luma and shared chroma palette sizes.
+ ///
+ /// The luma palette size.
+ /// The palette size shared by the chroma planes.
public void SetPaletteSizes(int ySize, int uvSize) => this.paletteSize = [ySize, uvSize];
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ComponentType.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ComponentType.cs
index b8546e72b..2490f254c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ComponentType.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ComponentType.cs
@@ -3,12 +3,38 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Identifies the luma or chroma component class used by AV1 entropy contexts.
+///
internal enum Av1ComponentType
{
- Luminance = 0, // luma
- Chroma = 1, // chroma (Cb+Cr)
- ChromaCb = 2, // chroma Cb
- ChromaCr = 3, // chroma Cr
- All = 4, // Y+Cb+Cr
+ ///
+ /// The luma component.
+ ///
+ Luminance = 0,
+
+ ///
+ /// Both chroma components.
+ ///
+ Chroma = 1,
+
+ ///
+ /// The blue-difference chroma component.
+ ///
+ ChromaCb = 2,
+
+ ///
+ /// The red-difference chroma component.
+ ///
+ ChromaCr = 3,
+
+ ///
+ /// The luma and both chroma components.
+ ///
+ All = 4,
+
+ ///
+ /// No component.
+ ///
None = 15
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs
index 482b5c26b..949e5f23c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs
@@ -5,27 +5,63 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores encoder-selected prediction, transform, skip, and palette state for one block.
+///
internal class Av1EncoderBlockModeInfo
{
+ ///
+ /// Gets the encoded block size.
+ ///
public Av1BlockSize BlockSize { get; }
+ ///
+ /// Gets the selected luma prediction mode.
+ ///
public Av1PredictionMode PredictionMode { get; }
+ ///
+ /// Gets the partition type that produced the block.
+ ///
public Av1PartitionType PartitionType { get; }
+ ///
+ /// Gets the selected chroma prediction mode.
+ ///
public Av1PredictionMode UvPredictionMode { get; }
+ ///
+ /// Gets a value indicating whether residual coefficients are omitted for the block.
+ ///
public bool Skip { get; } = true;
+ ///
+ /// Gets a value indicating whether compound skip mode is selected.
+ ///
public bool SkipMode { get; } = true;
+ ///
+ /// Gets a value indicating whether intra block copy is selected.
+ ///
public bool UseIntraBlockCopy { get; } = true;
+ ///
+ /// Gets the segmentation identifier assigned to the block.
+ ///
public int SegmentId { get; }
+ ///
+ /// Gets or sets the transform-tree depth selected for the block.
+ ///
public int TransformDepth { get; internal set; }
+ ///
+ /// Gets or sets the luma prediction mode written for the block.
+ ///
public Av1PredictionMode Mode { get; internal set; }
+ ///
+ /// Gets or sets the chroma prediction mode written for the block.
+ ///
public Av1PredictionMode UvMode { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs
index cd988c96e..a0e730c75 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs
@@ -3,21 +3,48 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores encoder block geometry and its selected coding-mode information.
+///
internal class Av1EncoderBlockStruct
{
+ ///
+ /// Gets the transform-unit state in transform traversal order.
+ ///
public Av1TransformUnit[] TransformBlocks { get; } = new Av1TransformUnit[Av1Constants.MaxTransformUnitCount];
+ ///
+ /// Gets or sets the macroblock edge and neighbor state used while writing the block.
+ ///
public required Av1MacroBlockD MacroBlock { get; set; }
+ ///
+ /// Gets or sets the index used to resolve the block geometry from mode-decision scan order.
+ ///
public int ModeDecisionScanIndex { get; set; }
+ ///
+ /// Gets or sets the quantizer index used for the block.
+ ///
public int QuantizationIndex { get; set; }
+ ///
+ /// Gets or sets the segmentation identifier assigned to the block.
+ ///
public int SegmentId { get; set; }
+ ///
+ /// Gets or sets the filter-intra mode selected for the block.
+ ///
public Av1FilterIntraMode FilterIntraMode { get; set; }
+ ///
+ /// Gets or sets the palette size for luma and for the shared chroma mode.
+ ///
public required int[] PaletteSize { get; internal set; }
+ ///
+ /// Gets or sets the encoder prediction-unit state for the block.
+ ///
public required Av1EncoderPredictionUnit[] PredictionUnits { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderCommon.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderCommon.cs
index 6efa09128..90eb8b152 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderCommon.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderCommon.cs
@@ -5,15 +5,33 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Holds sequence, frame, and macroblock state shared across AV1 encoder stages.
+///
internal class Av1EncoderCommon
{
+ ///
+ /// Gets or sets the frame height in 4x4 mode-information units.
+ ///
public int ModeInfoRowCount { get; internal set; }
+ ///
+ /// Gets or sets the frame width in 4x4 mode-information units.
+ ///
public int ModeInfoColumnCount { get; internal set; }
+ ///
+ /// Gets or sets the row stride of frame mode information in 4x4 units.
+ ///
public int ModeInfoStride { get; internal set; }
+ ///
+ /// Gets or sets the coded frame dimensions.
+ ///
public required ObuFrameSize FrameSize { get; internal set; }
+ ///
+ /// Gets or sets the tile layout for the current frame.
+ ///
public required ObuTileGroupHeader TilesInfo { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs
index 5b46389ef..a57247f2c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs
@@ -3,11 +3,23 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores encoder-selected intra prediction modes and directional-angle adjustments for one block.
+///
internal class Av1EncoderPredictionUnit
{
+ ///
+ /// Gets or sets the directional angle adjustment for each prediction plane.
+ ///
public required byte[] AngleDelta { get; set; }
+ ///
+ /// Gets or sets the chroma-from-luma alpha magnitude index.
+ ///
public int ChromaFromLumaIndex { get; internal set; }
+ ///
+ /// Gets or sets the packed chroma-from-luma alpha signs for the U and V planes.
+ ///
public int ChromaFromLumaSigns { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EntropyCodingContext.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EntropyCodingContext.cs
index ead8f4462..a0873b1db 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EntropyCodingContext.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EntropyCodingContext.cs
@@ -3,16 +3,34 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Provides tile-writer operations that maintain encoder entropy-neighbor state.
+///
internal partial class Av1TileWriter
{
+ ///
+ /// Tracks the mode and coefficient positions while entropy-coding one AV1 superblock.
+ ///
internal class Av1EntropyCodingContext
{
+ ///
+ /// Gets or sets the macroblock mode information currently being encoded.
+ ///
public required Av1MacroBlockModeInfo MacroBlockModeInfo { get; internal set; }
+ ///
+ /// Gets or sets the pixel origin of the current superblock.
+ ///
public Point SuperblockOrigin { get; internal set; }
+ ///
+ /// Gets or sets the number of luma coefficient positions consumed in the current superblock.
+ ///
public int CodedAreaSuperblock { get; internal set; }
+ ///
+ /// Gets or sets the number of chroma coefficient positions consumed in the current superblock.
+ ///
public int CodedAreaSuperblockUv { get; internal set; }
}
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraMode.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraMode.cs
index f536982e0..17c08e5ac 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraMode.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraMode.cs
@@ -3,12 +3,38 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Identifies the filter-intra predictor kernel selected for an AV1 block.
+///
internal enum Av1FilterIntraMode
{
+ ///
+ /// The filter-intra DC predictor.
+ ///
DC,
+
+ ///
+ /// The filter-intra vertical predictor.
+ ///
Vertical,
+
+ ///
+ /// The filter-intra horizontal predictor.
+ ///
Horizontal,
+
+ ///
+ /// The filter-intra 157-degree directional predictor.
+ ///
Directional157,
+
+ ///
+ /// The filter-intra Paeth predictor.
+ ///
Paeth,
+
+ ///
+ /// The number of filter-intra modes.
+ ///
AllFilterIntraModes,
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraModeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraModeExtensions.cs
index 0a2e2e832..c27af0242 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraModeExtensions.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FilterIntraModeExtensions.cs
@@ -5,11 +5,22 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Provides validity checks for AV1 filter-intra modes.
+///
internal static class Av1FilterIntraModeExtensions
{
+ ///
+ /// Maps filter-intra syntax values to the directional mode used by the predictor.
+ ///
private static readonly Av1PredictionMode[] IntraDirection =
[Av1PredictionMode.DC, Av1PredictionMode.Vertical, Av1PredictionMode.Horizontal, Av1PredictionMode.Directional157Degrees, Av1PredictionMode.DC];
+ ///
+ /// Gets the intra-prediction direction associated with the specified filter-intra mode.
+ ///
+ /// The filter-intra mode.
+ /// The corresponding intra-prediction direction.
public static Av1PredictionMode ToIntraDirection(this Av1FilterIntraMode mode)
=> IntraDirection[(int)mode];
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs
index ee2bfb2f7..f8ff7b691 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameInfo.cs
@@ -6,35 +6,113 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
///
-/// Collection of all information for a single frame.
+/// Owns the mode, transform, coefficient, quantizer, and filter state decoded for one AV1 frame.
///
internal partial class Av1FrameInfo
{
- // Number of Coefficients in a single ModeInfo 4x4 block of pixels (1 length + 4 x 4).
+ ///
+ /// The coefficient slots reserved for one 4x4 mode-information unit: one end index followed by 16 coefficients.
+ ///
public const int CoefficientCountPerModeInfo = 1 + 16;
+ ///
+ /// Stores raster-ordered luma coefficients for every frame superblock.
+ ///
private readonly int[] coefficientsY = [];
+
+ ///
+ /// Stores raster-ordered blue-difference chroma coefficients for every frame superblock.
+ ///
private readonly int[] coefficientsU = [];
+
+ ///
+ /// Stores raster-ordered red-difference chroma coefficients for every frame superblock.
+ ///
private readonly int[] coefficientsV = [];
+
+ ///
+ /// The width and height of a superblock in 4x4 mode-information units.
+ ///
private readonly int modeInfoSizePerSuperblock;
+
+ ///
+ /// The number of 4x4 mode-information positions in one square superblock.
+ ///
private readonly int modeInfoCountPerSuperblock;
+
+ ///
+ /// The number of columns in the frame superblock grid.
+ ///
private readonly int superblockColumnCount;
+
+ ///
+ /// The number of rows in the frame superblock grid.
+ ///
private readonly int superblockRowCount;
+
+ ///
+ /// The base-2 reduction from luma coefficient capacity to per-chroma-plane capacity.
+ ///
private readonly int subsamplingFactor;
+
+ ///
+ /// Stores one addressing view for each frame superblock.
+ ///
private readonly Av1SuperblockInfo[] superblockInfos;
+
+ ///
+ /// Stores decoded block mode information in bitstream traversal order.
+ ///
private readonly Av1BlockModeInfo[] modeInfos;
+
+ ///
+ /// Maps every frame-relative 4x4 position to its covering entry in .
+ ///
private readonly Av1FrameModeInfoMap modeInfoMap;
+
+ ///
+ /// Stores luma transform information grouped by superblock.
+ ///
private readonly Av1TransformInfo[] transformInfosY;
+
+ ///
+ /// Stores both chroma planes' transform information grouped by superblock.
+ ///
private readonly Av1TransformInfo[] transformInfosUv;
+
+ ///
+ /// Stores the quantizer-index delta for each frame superblock.
+ ///
private readonly int[] deltaQ;
+
+ ///
+ /// The base-2 number of constrained directional enhancement filter entries allocated per superblock.
+ ///
private readonly int cdefStrengthFactorLog2;
+
+ ///
+ /// Stores constrained directional enhancement filter strengths grouped by superblock.
+ ///
private readonly int[] cdefStrength;
+
+ ///
+ /// The base-2 number of loop-filter delta values stored per superblock.
+ ///
private readonly int deltaLoopFactorLog2 = 2;
+
+ ///
+ /// Stores the four loop-filter delta values for each superblock.
+ ///
private readonly int[] deltaLoopFilter;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The sequence header defining maximum dimensions, superblock size, and color sampling.
public Av1FrameInfo(ObuSequenceHeader sequenceHeader)
{
- // init_main_frame_ctxt
+ // Size frame-owned storage from the sequence maximums because later frame headers may select
+ // any coded dimensions up to these bounds without rebuilding the decoder's indexing model.
int superblockSizeLog2 = sequenceHeader.SuperblockSizeLog2;
int superblockAlignedWidth = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, superblockSizeLog2);
int superblockAlignedHeight = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameHeight, superblockSizeLog2);
@@ -45,14 +123,15 @@ internal partial class Av1FrameInfo
this.modeInfoCountPerSuperblock = this.modeInfoSizePerSuperblock * this.modeInfoSizePerSuperblock;
int numPlanes = sequenceHeader.ColorConfig.IsMonochrome ? 1 : Av1Constants.MaxPlanes;
- // Allocate the arrays.
+ // A decoded block can cover multiple 4x4 positions, so modeInfos stores each block once while
+ // modeInfoMap makes every covered position resolve to that single traversal-order entry.
this.superblockInfos = new Av1SuperblockInfo[superblockCount];
this.modeInfos = new Av1BlockModeInfo[superblockCount * this.modeInfoCountPerSuperblock];
this.modeInfoMap = new Av1FrameModeInfoMap(new Size(this.modeInfoSizePerSuperblock * this.superblockColumnCount, this.modeInfoSizePerSuperblock * this.superblockRowCount));
this.transformInfosY = new Av1TransformInfo[superblockCount * this.modeInfoCountPerSuperblock];
this.transformInfosUv = new Av1TransformInfo[2 * superblockCount * this.modeInfoCountPerSuperblock];
- // Initialize the arrays.
+ // Superblock views retain only their grid position and address all storage through this owner.
int i = 0;
for (int y = 0; y < this.superblockRowCount; y++)
{
@@ -67,7 +146,7 @@ internal partial class Av1FrameInfo
bool subX = sequenceHeader.ColorConfig.SubSamplingX;
bool subY = sequenceHeader.ColorConfig.SubSamplingY;
- // Factor: 444 => 0, 422 => 1, 420 => 2.
+ // Chroma capacity scales by two for each sampled axis: 4:4:4 => 0, 4:2:2 => 1, 4:2:0 => 2.
this.subsamplingFactor = (subX && subY) ? 2 : (subX && !subY) ? 1 : (!subX && !subY) ? 0 : -1;
Guard.IsFalse(this.subsamplingFactor == -1, nameof(this.subsamplingFactor), "Invalid combination of subsampling.");
int lumaCoefficientCountPerSuperblock = this.modeInfoCountPerSuperblock * CoefficientCountPerModeInfo;
@@ -77,7 +156,7 @@ internal partial class Av1FrameInfo
this.coefficientsV = new int[superblockCount * chromaCoefficientCountPerSuperblock];
this.deltaQ = new int[superblockCount];
- // Superblock size: 128x128 has sizelog2 = 7, 64x64 = 6. Factor should be 128x128 => 4 and 64x64 => 1.
+ // A 128x128 superblock contains four 64x64 CDEF filter blocks; a 64x64 superblock contains one.
this.cdefStrengthFactorLog2 = (superblockSizeLog2 - 6) << 2;
this.cdefStrength = new int[superblockCount << this.cdefStrengthFactorLog2];
Array.Fill(this.cdefStrength, -1);
@@ -85,15 +164,20 @@ internal partial class Av1FrameInfo
}
///
- /// Gets the number of mode info blocks in a single superblock.
+ /// Gets the total mode-information capacity allocated for the frame.
///
public int ModeInfoCount => this.modeInfos.Length;
///
- /// Gets the Width or height of a single superblock, counted in mode info blocks.
+ /// Gets the width or height of one square superblock in 4x4 mode-information units.
///
public int SuperblockModeInfoSize => this.modeInfoSizePerSuperblock;
+ ///
+ /// Gets the superblock view at the specified frame-grid position.
+ ///
+ /// The position in the frame superblock grid.
+ /// The superblock view.
public Av1SuperblockInfo GetSuperblock(Point index)
{
Span span = this.superblockInfos;
@@ -101,8 +185,19 @@ internal partial class Av1FrameInfo
return span[i];
}
+ ///
+ /// Gets the mode information covering the origin of a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// The mode information covering the superblock origin.
public Av1BlockModeInfo GetModeInfo(Point superblockIndex) => this.GetModeInfo(superblockIndex, Point.Empty);
+ ///
+ /// Gets the mode information covering a position relative to a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// The position within the superblock in 4x4 mode-information units.
+ /// The mode information covering the position.
public Av1BlockModeInfo GetModeInfo(Point superblockIndex, Point modeInfoIndex)
{
Point location = this.GetModeInfoPosition(superblockIndex, modeInfoIndex);
@@ -113,11 +208,16 @@ internal partial class Av1FrameInfo
///
/// Gets the mode information record covering the specified frame-relative mode information position.
///
+ /// The frame-relative position in 4x4 mode-information units.
+ /// The mode information covering the position.
public Av1BlockModeInfo GetModeInfoAt(Point modeInfoPosition) => this.modeInfos[this.modeInfoMap[modeInfoPosition]];
///
/// Gets the mode information records parsed for the specified superblock in bitstream order.
///
+ /// The position in the frame superblock grid.
+ /// The number of parsed records to return.
+ /// The parsed mode-information records.
public Span GetModeInfos(Point superblockIndex, int count)
{
Point location = this.GetModeInfoPosition(superblockIndex, Point.Empty);
@@ -125,6 +225,12 @@ internal partial class Av1FrameInfo
return this.modeInfos.AsSpan(index, count);
}
+ ///
+ /// Gets the transform-information storage for one plane of a specified superblock.
+ ///
+ /// The zero-based plane index.
+ /// The position in the frame superblock grid.
+ /// The luma storage for plane zero; otherwise, the shared chroma storage.
public Span GetSuperblockTransform(int plane, Point index)
{
if (plane == 0)
@@ -135,6 +241,11 @@ internal partial class Av1FrameInfo
return this.GetSuperblockTransformUv(index);
}
+ ///
+ /// Gets the luma transform-information storage for a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// The superblock luma transform-information span.
public Span GetSuperblockTransformY(Point index)
{
Span span = this.transformInfosY;
@@ -142,6 +253,11 @@ internal partial class Av1FrameInfo
return span.Slice(offset, this.modeInfoCountPerSuperblock);
}
+ ///
+ /// Gets the shared chroma transform-information storage for a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// The superblock chroma transform-information span.
public Span GetSuperblockTransformUv(Point index)
{
Span span = this.transformInfosUv;
@@ -149,6 +265,11 @@ internal partial class Av1FrameInfo
return span.Slice(offset, this.modeInfoCountPerSuperblock << 1);
}
+ ///
+ /// Gets the luma coefficient storage for a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// The superblock luma coefficient span.
public Span GetCoefficientsY(Point index)
{
Span span = this.coefficientsY;
@@ -157,6 +278,11 @@ internal partial class Av1FrameInfo
return span.Slice(superblock * count, count);
}
+ ///
+ /// Gets the blue-difference chroma coefficient storage for a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// The superblock blue-difference chroma coefficient span.
public Span GetCoefficientsU(Point index)
{
Span span = this.coefficientsU;
@@ -165,6 +291,11 @@ internal partial class Av1FrameInfo
return span.Slice(superblock * count, count);
}
+ ///
+ /// Gets the red-difference chroma coefficient storage for a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// The superblock red-difference chroma coefficient span.
public Span GetCoefficientsV(Point index)
{
Span span = this.coefficientsV;
@@ -173,6 +304,11 @@ internal partial class Av1FrameInfo
return span.Slice(superblock * count, count);
}
+ ///
+ /// Gets a reference to the quantizer-index delta for a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// A reference to the superblock quantizer-index delta.
public ref int GetDeltaQuantizationIndex(Point index)
{
Span span = this.deltaQ;
@@ -180,6 +316,11 @@ internal partial class Av1FrameInfo
return ref span[i];
}
+ ///
+ /// Gets the constrained directional enhancement filter strengths for a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// The superblock filter-strength span.
public Span GetCdefStrength(Point index)
{
Span span = this.cdefStrength;
@@ -187,6 +328,10 @@ internal partial class Av1FrameInfo
return span.Slice(i, 1 << this.cdefStrengthFactorLog2);
}
+ ///
+ /// Resets every constrained directional enhancement filter strength for a superblock to its unassigned value.
+ ///
+ /// The position in the frame superblock grid.
internal void ClearCdef(Point index)
{
Span cdefs = this.GetCdefStrength(index);
@@ -196,6 +341,11 @@ internal partial class Av1FrameInfo
}
}
+ ///
+ /// Gets the four loop-filter delta values for a specified superblock.
+ ///
+ /// The position in the frame superblock grid.
+ /// The superblock loop-filter delta span.
public Span GetDeltaLoopFilter(Point index)
{
Span span = this.deltaLoopFilter;
@@ -203,14 +353,28 @@ internal partial class Av1FrameInfo
return span.Slice(i, 1 << this.deltaLoopFactorLog2);
}
+ ///
+ /// Resets all frame loop-filter delta values to zero.
+ ///
public void ClearDeltaLoopFilter() => Array.Fill(this.deltaLoopFilter, 0);
+ ///
+ /// Stores decoded mode information and maps every 4x4 position covered by its block.
+ ///
+ /// The decoded block mode information.
+ /// The containing superblock.
public void UpdateModeInfo(Av1BlockModeInfo modeInfo, Av1SuperblockInfo superblockInfo)
{
this.modeInfos[this.modeInfoMap.NextIndex] = modeInfo;
this.modeInfoMap.Update(this.GetModeInfoPosition(superblockInfo.Position, modeInfo.PositionInSuperblock), modeInfo.BlockSize);
}
+ ///
+ /// Converts a superblock-relative mode-information position to frame-relative coordinates.
+ ///
+ /// The position in the frame superblock grid.
+ /// The position within the superblock in 4x4 units.
+ /// The frame-relative position in 4x4 mode-information units.
private Point GetModeInfoPosition(Point superblockPosition, Point positionInSuperblock)
{
int x = (superblockPosition.X * this.modeInfoSizePerSuperblock) + positionInSuperblock.X;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameModeInfoMap.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameModeInfoMap.cs
index c2448e5a0..97427b20f 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameModeInfoMap.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1FrameModeInfoMap.cs
@@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Provides frame-wide lookup and storage for decoded AV1 mode-information blocks.
+///
internal partial class Av1FrameInfo
{
///
@@ -13,9 +16,20 @@ internal partial class Av1FrameInfo
///
public class Av1FrameModeInfoMap
{
+ ///
+ /// Stores the mode-information index assigned to each aligned 4x4 frame location.
+ ///
private readonly ushort[] offsets;
+
+ ///
+ /// The dimensions of in 4x4 mode-information units.
+ ///
private readonly Size alignedModeInfoCount;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The aligned frame dimensions in 4x4 mode-information units.
public Av1FrameModeInfoMap(Size modeInfoCount)
{
this.alignedModeInfoCount = modeInfoCount;
@@ -29,8 +43,9 @@ internal partial class Av1FrameInfo
public int NextIndex { get; private set; }
///
- /// Gets the mapped index for the given location.
+ /// Gets the mode-information index mapped to the specified 4x4 location.
///
+ /// The location in 4x4 mode-information units.
public int this[Point location]
{
get
@@ -40,16 +55,22 @@ internal partial class Av1FrameInfo
}
}
+ ///
+ /// Maps every 4x4 location covered by a decoded block to the next mode-information index.
+ ///
+ /// The block origin in 4x4 mode-information units.
+ /// The decoded block size.
public void Update(Point modeInfoLocation, Av1BlockSize blockSize)
{
- // Equivalent in SVT-Av1: EbDecNbr.c svt_aom_update_block_nbrs
int bw4 = blockSize.Get4x4WideCount();
int bh4 = blockSize.Get4x4HighCount();
DebugGuard.MustBeGreaterThanOrEqualTo(modeInfoLocation.Y, 0, nameof(modeInfoLocation));
DebugGuard.MustBeLessThanOrEqualTo(modeInfoLocation.Y + bh4, this.alignedModeInfoCount.Height, nameof(modeInfoLocation));
DebugGuard.MustBeGreaterThanOrEqualTo(modeInfoLocation.X, 0, nameof(modeInfoLocation));
DebugGuard.MustBeLessThanOrEqualTo(modeInfoLocation.X + bw4, this.alignedModeInfoCount.Width, nameof(modeInfoLocation));
- /* Update 4x4 nbr offset map */
+
+ // Every 4x4 cell covered by the block must resolve to the same mode information,
+ // because later blocks query their above and left neighbors at cell granularity.
for (int i = modeInfoLocation.Y; i < modeInfoLocation.Y + bh4; i++)
{
Array.Fill(this.offsets, (ushort)this.NextIndex, (i * this.alignedModeInfoCount.Width) + modeInfoLocation.X, bw4);
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1IntraFilterModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1IntraFilterModeInfo.cs
index 649a069b0..52db23dd4 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1IntraFilterModeInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1IntraFilterModeInfo.cs
@@ -3,9 +3,18 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores whether filter-intra prediction is active and which filter-intra mode is selected.
+///
internal class Av1IntraFilterModeInfo
{
+ ///
+ /// Gets or sets a value indicating whether filter-intra prediction is enabled for the block.
+ ///
public bool UseFilterIntra { get; set; }
+ ///
+ /// Gets or sets the filter-intra mode selected for the block.
+ ///
public Av1FilterIntraMode Mode { get; set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs
index a21116b6f..84bbe9cfb 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1LevelBuffer.cs
@@ -8,29 +8,61 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Owns the padded absolute-coefficient level plane used to derive AV1 coefficient entropy contexts.
+///
internal sealed class Av1LevelBuffer : IDisposable
{
+ ///
+ /// Owns the padded level storage until the buffer is disposed.
+ ///
private IMemoryOwner? memory;
+ ///
+ /// Initializes a new instance of the class for the maximum AV1 transform size.
+ ///
+ /// The configuration providing the memory allocator.
public Av1LevelBuffer(Configuration configuration)
: this(configuration, new Size(Av1Constants.MaxTransformSize, Av1Constants.MaxTransformSize))
{
}
+ ///
+ /// Initializes a new instance of the class for the specified coefficient dimensions.
+ ///
+ /// The configuration providing the memory allocator.
+ /// The unpadded coefficient dimensions.
public Av1LevelBuffer(Configuration configuration, Size size)
{
this.Size = size;
+
+ // Coefficient-context derivation reads fixed neighboring offsets around the coded transform.
+ // Keeping those offsets inside one clean allocation avoids branches at transform boundaries.
int totalHeight = Av1Constants.TransformPadTop + size.Height + Av1Constants.TransformPadBottom;
this.Stride = Av1Constants.TransformPadHorizontal + size.Width;
this.memory = configuration.MemoryAllocator.Allocate(this.Stride * totalHeight, AllocationOptions.Clean);
}
+ ///
+ /// Gets the unpadded coefficient dimensions.
+ ///
public Size Size { get; }
+ ///
+ /// Gets the padded row stride in bytes.
+ ///
public int Stride { get; }
+ ///
+ /// Gets the coefficient level at the specified unpadded position.
+ ///
+ /// The coefficient position.
public int this[Point position] => this.GetRow(position.Y)[position.X];
+ ///
+ /// Initializes the unpadded level plane from raster-ordered coefficient magnitudes.
+ ///
+ /// The coefficient levels to copy.
public void Initialize(Span coefficientBuffer)
{
ObjectDisposedException.ThrowIf(this.memory == null, this);
@@ -41,6 +73,7 @@ internal sealed class Av1LevelBuffer : IDisposable
ref int sourceRef = ref coefficientBuffer[y * this.Size.Width];
for (int x = 0; x < this.Size.Width; x++)
{
+ // Entropy contexts use a saturated byte-level summary rather than the full coefficient magnitude.
destRef = (byte)Av1Math.Clamp(sourceRef, 0, byte.MaxValue);
destRef = ref Unsafe.Add(ref destRef, 1);
sourceRef = ref Unsafe.Add(ref sourceRef, 1);
@@ -48,6 +81,11 @@ internal sealed class Av1LevelBuffer : IDisposable
}
}
+ ///
+ /// Converts a raster-order coefficient index to its two-dimensional position.
+ ///
+ /// The raster-order coefficient index.
+ /// The corresponding coefficient position.
public Point GetPosition(int index)
{
int x = index % this.Size.Width;
@@ -55,9 +93,19 @@ internal sealed class Av1LevelBuffer : IDisposable
return new Point(x, y);
}
+ ///
+ /// Gets a padded coefficient row for the specified position.
+ ///
+ /// A position whose vertical coordinate selects the row.
+ /// The selected row, including its horizontal context padding.
public Span GetRow(Point pos)
=> this.GetRow(pos.Y);
+ ///
+ /// Gets a padded coefficient row by its unpadded vertical coordinate.
+ ///
+ /// The row coordinate, which may address the top context padding.
+ /// The selected row, including its horizontal context padding.
public Span GetRow(int y)
{
ObjectDisposedException.ThrowIf(this.memory == null, this);
@@ -66,12 +114,16 @@ internal sealed class Av1LevelBuffer : IDisposable
return this.memory.Memory.Span.Slice(row * this.Stride, this.Size.Width + Av1Constants.TransformPadHorizontal);
}
+ ///
public void Dispose()
{
this.memory?.Dispose();
this.memory = null;
}
+ ///
+ /// Clears all coefficient levels and context padding.
+ ///
internal void Clear()
{
ObjectDisposedException.ThrowIf(this.memory == null, this);
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs
index bb69f9ee8..e7edd0622 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs
@@ -3,53 +3,87 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Holds decoder-side macroblock edges, plane state, and neighboring mode information.
+///
internal class Av1MacroBlockD
{
+ ///
+ /// Stores the mode-information entries exposed through .
+ ///
private Av1ModeInfo[] modeInfo = [];
+ ///
+ /// Gets or sets the mode-information entries for the current block and its mapped neighbors.
+ ///
public required Span ModeInfo
{
get => this.modeInfo;
internal set
{
+ // A span cannot be retained by the class, so preserve the selected map entries in owned storage.
this.modeInfo = new Av1ModeInfo[value.Length];
value.CopyTo(this.modeInfo);
}
}
+ ///
+ /// Gets or sets the tile containing the current block.
+ ///
public required Av1TileInfo Tile { get; internal set; }
+ ///
+ /// Gets or sets a value indicating whether an above block is available within the tile.
+ ///
public bool IsUpAvailable { get; internal set; }
+ ///
+ /// Gets or sets a value indicating whether a left block is available within the tile.
+ ///
public bool IsLeftAvailable { get; internal set; }
+ ///
+ /// Gets or sets the above macroblock mode information, when available.
+ ///
public Av1MacroBlockModeInfo? AboveMacroBlock { get; internal set; }
+ ///
+ /// Gets or sets the left macroblock mode information, when available.
+ ///
public Av1MacroBlockModeInfo? LeftMacroBlock { get; internal set; }
+ ///
+ /// Gets or sets the row stride of the frame mode-information map.
+ ///
public int ModeInfoStride { get; internal set; }
///
- /// Gets or sets the number of macro blocks until the top edge.
+ /// Gets or sets the signed distance from the block to the top frame edge in one-eighth-sample units.
///
public int ToTopEdge { get; internal set; }
///
- /// Gets or sets the number of macro blocks until the bottom edge.
+ /// Gets or sets the signed distance from the block to the bottom frame edge in one-eighth-sample units.
///
public int ToBottomEdge { get; internal set; }
///
- /// Gets or sets the number of macro blocks until the left edge.
+ /// Gets or sets the signed distance from the block to the left frame edge in one-eighth-sample units.
///
public int ToLeftEdge { get; internal set; }
///
- /// Gets or sets the number of macro blocks until the right edge.
+ /// Gets or sets the signed distance from the block to the right frame edge in one-eighth-sample units.
///
public int ToRightEdge { get; internal set; }
+ ///
+ /// Gets or sets the block dimensions in samples for rectangular-partition context selection.
+ ///
public Size N8Size { get; internal set; }
+ ///
+ /// Gets or sets a value indicating whether this block is the second half of a rectangular partition.
+ ///
public bool IsSecondRectangle { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs
index 1d916a531..ba1f72fbe 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs
@@ -3,11 +3,23 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores the encoder's selected modes and references for an AV1 macroblock.
+///
internal class Av1MacroBlockModeInfo
{
+ ///
+ /// Gets or sets the prediction, transform, and segmentation decisions for the block.
+ ///
public required Av1EncoderBlockModeInfo Block { get; internal set; }
+ ///
+ /// Gets or sets the luma palette decisions for the block.
+ ///
public required Av1PaletteLumaModeInfo Palette { get; internal set; }
+ ///
+ /// Gets or sets the constrained directional enhancement filter strength for the block.
+ ///
public int CdefStrength { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ModeInfo.cs
index ba6f3b084..cdd6732c4 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ModeInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ModeInfo.cs
@@ -3,7 +3,13 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores the decoded prediction, segmentation, skip, and transform state for an AV1 mode-information block.
+///
internal class Av1ModeInfo
{
+ ///
+ /// Gets or sets the macroblock mode information associated with this map entry.
+ ///
public required Av1MacroBlockModeInfo MacroBlockModeInfo { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs
index f4e2957a5..c49b4f115 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs
@@ -6,15 +6,39 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores left, top, and top-left neighbor values at the granularity required by AV1 encoder contexts.
+///
+/// The context value type, including its invalid sentinel value.
internal class Av1NeighborArrayUnit
where T : struct, IMinMaxValue
{
+ ///
+ /// The sentinel used for neighbor positions that have not been populated.
+ ///
public static readonly T InvalidNeighborData = T.MaxValue;
+ ///
+ /// Stores context units exposed to blocks on the right.
+ ///
private readonly T[] left;
+
+ ///
+ /// Stores context units exposed to blocks below.
+ ///
private readonly T[] top;
+
+ ///
+ /// Stores context units indexed by the diagonal difference between horizontal and vertical positions.
+ ///
private readonly T[] topLeft;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The number of values in the left-neighbor storage.
+ /// The number of values in the top-neighbor storage.
+ /// The number of values in the diagonal-neighbor storage.
public Av1NeighborArrayUnit(int leftSize, int topSize, int topLeftSize)
{
this.left = new T[leftSize];
@@ -22,33 +46,87 @@ internal class Av1NeighborArrayUnit
this.topLeft = new T[topLeftSize];
}
+ ///
+ /// Selects which neighbor arrays receive an update.
+ ///
[Flags]
public enum UnitMask
{
+ ///
+ /// Update the left-neighbor storage.
+ ///
Left = 1,
+
+ ///
+ /// Update the top-neighbor storage.
+ ///
Top = 2,
+
+ ///
+ /// Update the top-left diagonal storage.
+ ///
TopLeft = 4,
}
+ ///
+ /// Gets the left-neighbor storage.
+ ///
public Span Left => this.left;
+ ///
+ /// Gets the top-neighbor storage.
+ ///
public Span Top => this.top;
+ ///
+ /// Gets the top-left diagonal storage.
+ ///
public Span TopLeft => this.topLeft;
+ ///
+ /// Gets or sets the base-2 logarithm of the top and left context granularity in samples.
+ ///
public required int GranularityNormalLog2 { get; set; }
+ ///
+ /// Gets or sets the base-2 logarithm of the diagonal context granularity in samples.
+ ///
public required int GranularityTopLeftLog2 { get; set; }
+ ///
+ /// Gets the number of consecutive values stored for each neighbor-array unit.
+ ///
public int UnitSize { get; private set; }
+ ///
+ /// Gets the left-neighbor unit index for a sample position.
+ ///
+ /// The sample position.
+ /// The left-neighbor unit index.
public int GetLeftIndex(Point loc) => loc.Y >> this.GranularityNormalLog2;
+ ///
+ /// Gets the top-neighbor unit index for a sample position.
+ ///
+ /// The sample position.
+ /// The top-neighbor unit index.
public int GetTopIndex(Point loc) => loc.X >> this.GranularityNormalLog2;
+ ///
+ /// Gets the diagonal-neighbor unit index for a sample position.
+ ///
+ /// The sample position.
+ /// The top-left neighbor index derived from the position's diagonal.
public int GetTopLeftIndex(Point loc)
=> this.left.Length + (loc.X >> this.GranularityTopLeftLog2) - (loc.Y >> this.GranularityTopLeftLog2);
+ ///
+ /// Writes one context unit across the selected block edges.
+ ///
+ /// The values that make up one context unit.
+ /// The block origin in samples.
+ /// The block dimensions in samples.
+ /// The neighbor arrays to update.
public void UnitModeWrite(ReadOnlySpan value, Point origin, Size blockSize, UnitMask mask)
{
int idx, j;
@@ -83,7 +161,7 @@ internal class Av1NeighborArrayUnit
for (idx = 0; idx < count; ++idx)
{
- /* svt_memcpy less that 10 bytes*/
+ // Unit sizes are deliberately tiny, so direct ref copies avoid slicing for every neighbor position.
for (j = 0; j < na_unit_size; ++j)
{
dst_ptr = value[j];
@@ -117,7 +195,7 @@ internal class Av1NeighborArrayUnit
for (idx = 0; idx < count; ++idx)
{
- /* svt_memcpy less that 10 bytes*/
+ // Unit sizes are deliberately tiny, so direct ref copies avoid slicing for every neighbor position.
for (j = 0; j < na_unit_size; ++j)
{
dst_ptr = value[j];
@@ -156,7 +234,7 @@ internal class Av1NeighborArrayUnit
for (idx = 0; idx < count; ++idx)
{
- /* svt_memcpy less that 10 bytes*/
+ // Unit sizes are deliberately tiny, so direct ref copies avoid slicing for every neighbor position.
for (j = 0; j < na_unit_size; ++j)
{
dst_ptr = value[j];
@@ -166,5 +244,13 @@ internal class Av1NeighborArrayUnit
}
}
+ ///
+ /// Writes a DC-sign context across selected block edges.
+ ///
+ /// The encoded DC-sign context.
+ /// The block origin in samples.
+ /// The block dimensions in samples.
+ /// The neighbor arrays to update.
+ /// The byte-specific write path is not implemented.
internal void UnitModeWrite(Span dcSignSpan, Point blockOrigin, Size blockSize, Av1NeighborArrayUnit.UnitMask unitMask) => throw new NotImplementedException();
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PaletteLumaModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PaletteLumaModeInfo.cs
index aa1e1e94f..67e9e1b81 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PaletteLumaModeInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PaletteLumaModeInfo.cs
@@ -3,6 +3,9 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Reserves encoder-side state for AV1 luma palette mode decisions.
+///
internal class Av1PaletteLumaModeInfo
{
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseAboveNeighbor4x4Context.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseAboveNeighbor4x4Context.cs
index 0f6c4149c..91442267f 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseAboveNeighbor4x4Context.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseAboveNeighbor4x4Context.cs
@@ -6,21 +6,36 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores entropy, palette, partition, and transform contexts for 4-by-4 blocks above the current block.
+///
internal class Av1ParseAboveNeighbor4x4Context
{
- /* Buffer holding the sign of the DC coefficients and the
- cumulative sum of the coefficient levels of the above 4x4
- blocks corresponding to the current super block row. */
+ ///
+ /// Stores DC-sign and cumulative coefficient-level contexts for each plane above the current block.
+ ///
private readonly int[][] aboveContext = new int[Av1Constants.MaxPlanes][];
- /* Buffer holding the seg_id_predicted of the previous 4x4 block row. */
+ ///
+ /// Stores segmentation-prediction contexts from the preceding 4x4 row.
+ ///
private readonly int[] aboveSegmentIdPredictionContext;
- /* Value of base colors for Y, U, and V */
+ ///
+ /// Stores palette base colors for each plane above the current block.
+ ///
private readonly int[][] abovePaletteColors = new int[Av1Constants.MaxPlanes][];
+ ///
+ /// Stores compound-reference group contexts from the preceding 4x4 row.
+ ///
private readonly int[] aboveCompGroupIndex;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The number of color planes.
+ /// The frame width in 4x4 mode-information columns.
public Av1ParseAboveNeighbor4x4Context(int planesCount, int modeInfoColumnCount)
{
int wide64x64Count = Av1BlockSize.Block64x64.Get4x4WideCount();
@@ -46,8 +61,19 @@ internal class Av1ParseAboveNeighbor4x4Context
///
public int[] AboveTransformWidth { get; }
+ ///
+ /// Gets the coefficient context row for the specified plane.
+ ///
+ /// The zero-based plane index.
+ /// The coefficient contexts for the plane.
public int[] GetContext(int plane) => this.aboveContext[plane];
+ ///
+ /// Resets above-neighbor state for the active tile-column range.
+ ///
+ /// The sequence header describing the color planes.
+ /// The first mode-information column in the tile.
+ /// The exclusive end mode-information column in the tile.
public void Clear(ObuSequenceHeader sequenceHeader, int modeInfoColumnStart, int modeInfoColumnEnd)
{
int planeCount = sequenceHeader.ColorConfig.PlaneCount;
@@ -64,8 +90,16 @@ internal class Av1ParseAboveNeighbor4x4Context
Array.Fill(this.aboveCompGroupIndex, 0, 0, width);
}
+ ///
+ /// Updates the above partition context for every 4x4 column covered by a block.
+ ///
+ /// The block origin in frame mode-information units.
+ /// The active tile boundaries.
+ /// The size produced by the decoded partition.
+ /// The parent block size.
public void UpdatePartition(Point modeInfoLocation, Av1TileInfo tileInfo, Av1BlockSize subSize, Av1BlockSize blockSize)
{
+ // Above contexts are tile-local even though block positions are frame-relative.
int startIndex = modeInfoLocation.X - tileInfo.ModeInfoColumnStart;
int bw = blockSize.Get4x4WideCount();
int value = Av1PartitionContext.GetAboveContext(subSize);
@@ -74,6 +108,14 @@ internal class Av1ParseAboveNeighbor4x4Context
Array.Fill(this.AbovePartitionWidth, value, startIndex, bw);
}
+ ///
+ /// Updates the above transform-size context for every 4x4 column covered by a block.
+ ///
+ /// The block origin in frame mode-information units.
+ /// The active tile boundaries.
+ /// The selected transform size.
+ /// The decoded block size.
+ /// A value indicating whether the block omits residual coefficients.
public void UpdateTransformation(Point modeInfoLocation, Av1TileInfo tileInfo, Av1TransformSize transformSize, Av1BlockSize blockSize, bool skip)
{
int startIndex = modeInfoLocation.X - tileInfo.ModeInfoColumnStart;
@@ -81,6 +123,7 @@ internal class Av1ParseAboveNeighbor4x4Context
int n4w = blockSize.Get4x4WideCount();
if (skip)
{
+ // Skipped blocks expose the full block width as their effective transform extent.
transformWidth = n4w << Av1Constants.ModeInfoSizeLog2;
}
@@ -88,6 +131,12 @@ internal class Av1ParseAboveNeighbor4x4Context
Array.Fill(this.AboveTransformWidth, transformWidth, startIndex, n4w);
}
+ ///
+ /// Clears a range of above coefficient contexts for one plane.
+ ///
+ /// The zero-based plane index.
+ /// The first context index to clear.
+ /// The number of context entries to clear.
internal void ClearContext(int plane, int offset, int length)
=> Array.Fill(this.aboveContext[plane], 0, offset, length);
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs
index 70f846d85..7159036ab 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ParseLeftNeighbor4x4Context.cs
@@ -6,21 +6,36 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores entropy, palette, partition, and transform contexts for 4-by-4 blocks left of the current block.
+///
internal class Av1ParseLeftNeighbor4x4Context
{
- /* Buffer holding the sign of the DC coefficients and the
- cumulative sum of the coefficient levels of the left 4x4
- blocks corresponding to the current super block row. */
+ ///
+ /// Stores DC-sign and cumulative coefficient-level contexts for each plane left of the current block.
+ ///
private readonly int[][] leftContext = new int[Av1Constants.MaxPlanes][];
- /* Buffer holding the seg_id_predicted of the previous 4x4 block row. */
+ ///
+ /// Stores segmentation-prediction contexts for the current superblock row.
+ ///
private readonly int[] leftSegmentIdPredictionContext;
- /* Value of base colors for Y, U, and V */
+ ///
+ /// Stores palette base colors for each plane left of the current block.
+ ///
private readonly int[][] leftPaletteColors = new int[Av1Constants.MaxPlanes][];
+ ///
+ /// Stores compound-reference group contexts for the current superblock row.
+ ///
private readonly int[] leftCompGroupIndex;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The number of color planes.
+ /// The superblock height in 4x4 mode-information rows.
public Av1ParseLeftNeighbor4x4Context(int planesCount, int superblockModeInfoSize)
{
this.LeftTransformHeight = new int[superblockModeInfoSize];
@@ -47,6 +62,10 @@ internal class Av1ParseLeftNeighbor4x4Context
///
public int[] LeftTransformHeight { get; }
+ ///
+ /// Resets all left-neighbor state for a new superblock row.
+ ///
+ /// The sequence header describing the superblock size and color planes.
public void Clear(ObuSequenceHeader sequenceHeader)
{
int blockCount = sequenceHeader.SuperblockModeInfoSize;
@@ -64,8 +83,16 @@ internal class Av1ParseLeftNeighbor4x4Context
Array.Fill(this.leftCompGroupIndex, 0, 0, blockCount);
}
+ ///
+ /// Updates the left partition context for every 4x4 row covered by a block.
+ ///
+ /// The block origin in frame mode-information units.
+ /// The active superblock location.
+ /// The size produced by the decoded partition.
+ /// The parent block size.
public void UpdatePartition(Point modeInfoLocation, Av1SuperblockInfo superblockInfo, Av1BlockSize subSize, Av1BlockSize blockSize)
{
+ // The left context is reused for each superblock row, so address it relative to the superblock origin.
int startIndex = (modeInfoLocation.Y - superblockInfo.ModeInfoPosition.Y) & Av1PartitionContext.Mask;
int bh = blockSize.Get4x4HighCount();
int value = Av1PartitionContext.GetLeftContext(subSize);
@@ -73,6 +100,14 @@ internal class Av1ParseLeftNeighbor4x4Context
Array.Fill(this.LeftPartitionHeight, value, startIndex, bh);
}
+ ///
+ /// Updates the left transform-size context for every 4x4 row covered by a block.
+ ///
+ /// The block origin in frame mode-information units.
+ /// The active superblock location.
+ /// The selected transform size.
+ /// The decoded block size.
+ /// A value indicating whether the block omits residual coefficients.
public void UpdateTransformation(Point modeInfoLocation, Av1SuperblockInfo superblockInfo, Av1TransformSize transformSize, Av1BlockSize blockSize, bool skip)
{
int startIndex = modeInfoLocation.Y - superblockInfo.ModeInfoPosition.Y;
@@ -80,6 +115,7 @@ internal class Av1ParseLeftNeighbor4x4Context
int n4h = blockSize.Get4x4HighCount();
if (skip)
{
+ // Skipped blocks expose the full block height as their effective transform extent.
transformHeight = n4h << Av1Constants.ModeInfoSizeLog2;
}
@@ -87,8 +123,19 @@ internal class Av1ParseLeftNeighbor4x4Context
Array.Fill(this.LeftTransformHeight, transformHeight, startIndex, n4h);
}
+ ///
+ /// Clears a range of left coefficient contexts for one plane.
+ ///
+ /// The zero-based plane index.
+ /// The first context index to clear.
+ /// The number of context entries to clear.
internal void ClearContext(int plane, int offset, int length)
=> Array.Fill(this.leftContext[plane], 0, offset, length);
+ ///
+ /// Gets the coefficient context column for the specified plane.
+ ///
+ /// The zero-based plane index.
+ /// The coefficient contexts for the plane.
internal int[] GetContext(int plane) => this.leftContext[plane];
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs
index 73b3d6757..e2fff6914 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs
@@ -5,35 +5,74 @@ using System.Numerics;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
-// Generates 5 bit field in which each bit set to 1 represents
-// a BlockSize partition 11111 means we split 128x128, 64x64, 32x32, 16x16
-// and 8x8. 10000 means we just split the 128x128 to 64x64
+///
+/// Stores the above and left five-bit AV1 partition contexts for a mode-information position.
+///
+///
+/// Each set bit records a split at one block-size level. For example, 11111 records splits from
+/// 128 by 128 through 8 by 8, while 10000 records only the 128 by 128 split.
+///
internal struct Av1PartitionContext : IMinMaxValue
{
+ ///
+ /// Maps each block size to the five-bit context stored for an above neighbor.
+ ///
private static readonly int[] AboveLookup =
[31, 31, 30, 30, 30, 28, 28, 28, 24, 24, 24, 16, 16, 16, 0, 0, 31, 28, 30, 24, 28, 16];
+ ///
+ /// Maps each block size to the five-bit context stored for a left neighbor.
+ ///
private static readonly int[] LeftLookup =
[31, 30, 31, 30, 28, 30, 28, 24, 28, 24, 16, 24, 16, 0, 16, 0, 28, 31, 24, 30, 16, 28];
- // Mask to extract ModeInfo offset within max ModeInfoBlock
+ ///
+ /// The mask used to convert a frame mode-information row to its position within a 128-sample superblock.
+ ///
public const int Mask = (1 << (7 - 2)) - 1;
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The context stored for blocks below this block.
+ /// The context stored for blocks to the right of this block.
public Av1PartitionContext(byte above, byte left)
{
this.Above = above;
this.Left = left;
}
+ ///
+ /// Gets the maximum representable partition context.
+ ///
public static Av1PartitionContext MaxValue => throw new NotImplementedException();
+ ///
+ /// Gets the minimum representable partition context.
+ ///
public static Av1PartitionContext MinValue => throw new NotImplementedException();
+ ///
+ /// Gets or sets the five-bit context derived from the left neighbor.
+ ///
public byte Left { get; internal set; }
+ ///
+ /// Gets or sets the five-bit context derived from the above neighbor.
+ ///
public byte Above { get; internal set; }
+ ///
+ /// Gets the above-neighbor partition context for the specified block size.
+ ///
+ /// The block size.
+ /// The five-bit above-neighbor context.
public static int GetAboveContext(Av1BlockSize blockSize) => AboveLookup[(int)blockSize];
+ ///
+ /// Gets the left-neighbor partition context for the specified block size.
+ ///
+ /// The block size.
+ /// The five-bit left-neighbor context.
public static int GetLeftContext(Av1BlockSize blockSize) => LeftLookup[(int)blockSize];
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionInfo.cs
index 5f6fadbd4..bb6862e3c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionInfo.cs
@@ -6,8 +6,18 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.ChromaFromLuma;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Describes a decoded AV1 partition's block geometry, neighbors, and frame-boundary availability.
+///
internal class Av1PartitionInfo
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The decoded mode information for the partition block.
+ /// The containing superblock.
+ /// A value indicating whether the partition has chroma samples.
+ /// The partition type that produced the block.
public Av1PartitionInfo(Av1BlockModeInfo modeInfo, Av1SuperblockInfo superblockInfo, bool isChroma, Av1PartitionType partitionType)
{
this.ModeInfo = modeInfo;
@@ -20,6 +30,9 @@ internal class Av1PartitionInfo
this.HeightInPixels = new int[3];
}
+ ///
+ /// Gets the decoded block mode information.
+ ///
public Av1BlockModeInfo ModeInfo { get; }
///
@@ -27,8 +40,14 @@ internal class Av1PartitionInfo
///
public Av1SuperblockInfo SuperblockInfo { get; }
+ ///
+ /// Gets a value indicating whether the partition has chroma samples at its current luma position.
+ ///
public bool IsChroma { get; }
+ ///
+ /// Gets the partition type that produced the block.
+ ///
public Av1PartitionType Type { get; }
///
@@ -61,32 +80,77 @@ internal class Av1PartitionInfo
///
public int RowIndex { get; set; }
+ ///
+ /// Gets or sets the mode information covering the immediately above luma neighbor.
+ ///
public Av1BlockModeInfo? AboveModeInfo { get; set; }
+ ///
+ /// Gets or sets the mode information covering the immediately left luma neighbor.
+ ///
public Av1BlockModeInfo? LeftModeInfo { get; set; }
+ ///
+ /// Gets or sets the mode information covering the above chroma neighbor.
+ ///
public Av1BlockModeInfo? AboveModeInfoForChroma { get; set; }
+ ///
+ /// Gets or sets the mode information covering the left chroma neighbor.
+ ///
public Av1BlockModeInfo? LeftModeInfoForChroma { get; set; }
+ ///
+ /// Gets or sets the constrained directional enhancement filter strengths associated with the block.
+ ///
public int[] CdefStrength { get; set; }
+ ///
+ /// Gets or sets the reference-frame identifiers selected for the block.
+ ///
public int[] ReferenceFrame { get; set; }
+ ///
+ /// Gets the signed distance from the block to the left frame edge in one-eighth-sample units.
+ ///
public int ModeBlockToLeftEdge { get; private set; }
+ ///
+ /// Gets the signed distance from the block to the right frame edge in one-eighth-sample units.
+ ///
public int ModeBlockToRightEdge { get; private set; }
+ ///
+ /// Gets the signed distance from the block to the top frame edge in one-eighth-sample units.
+ ///
public int ModeBlockToTopEdge { get; private set; }
+ ///
+ /// Gets the signed distance from the block to the bottom frame edge in one-eighth-sample units.
+ ///
public int ModeBlockToBottomEdge { get; private set; }
+ ///
+ /// Gets the block width in samples for each color plane.
+ ///
public int[] WidthInPixels { get; private set; }
+ ///
+ /// Gets the block height in samples for each color plane.
+ ///
public int[] HeightInPixels { get; private set; }
+ ///
+ /// Gets or sets the neighboring luma samples used by chroma-from-luma prediction.
+ ///
public Av1ChromaFromLumaContext? ChromaFromLumaContext { get; internal set; }
+ ///
+ /// Computes tile-neighbor availability, frame-edge distances, and per-plane block dimensions.
+ ///
+ /// The sequence header describing color subsampling.
+ /// The frame header describing coded dimensions.
+ /// The active tile boundaries.
public void ComputeBoundaryOffsets(ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader, Av1TileInfo tileInfo)
{
Av1BlockSize blockSize = this.ModeInfo.BlockSize;
@@ -105,21 +169,23 @@ internal class Av1PartitionInfo
this.ModeBlockToTopEdge = -this.RowIndex << shift;
this.ModeBlockToBottomEdge = (frameHeader.ModeInfoRowCount - bh4 - this.RowIndex) << shift;
- // Block Size width & height in pixels.
- // For Luma bock
+ // The bitstream expresses block size on the luma grid. Chroma dimensions are derived by
+ // subsampling that grid while retaining at least one 4x4 chroma unit for narrow blocks.
const int modeInfoSize = 1 << Av1Constants.ModeInfoSizeLog2;
this.WidthInPixels[0] = bw4 * modeInfoSize;
this.HeightInPixels[0] = bh4 * modeInfoSize;
- // For U plane chroma bock
this.WidthInPixels[1] = Math.Max(1, bw4 >> subX) * modeInfoSize;
this.HeightInPixels[1] = Math.Max(1, bh4 >> subY) * modeInfoSize;
- // For V plane chroma bock
this.WidthInPixels[2] = Math.Max(1, bw4 >> subX) * modeInfoSize;
this.HeightInPixels[2] = Math.Max(1, bh4 >> subY) * modeInfoSize;
}
+ ///
+ /// Resolves the decoded luma and chroma mode information for available above and left neighbors.
+ ///
+ /// The color-plane subsampling configuration.
public void PopulateModeInfoNeighbors(ObuColorConfig colorConfig)
{
if (this.AvailableAbove)
@@ -154,6 +220,12 @@ internal class Av1PartitionInfo
}
}
+ ///
+ /// Gets the block width clipped to the right frame edge.
+ ///
+ /// The luma block size.
+ /// A value indicating whether the target plane is horizontally subsampled.
+ /// The clipped width in 4x4 units of the target plane.
public int GetMaxBlockWide(Av1BlockSize blockSize, bool subX)
{
int maxBlockWide = blockSize.GetWidth();
@@ -166,6 +238,12 @@ internal class Av1PartitionInfo
return maxBlockWide >> 2;
}
+ ///
+ /// Gets the block height clipped to the bottom frame edge.
+ ///
+ /// The luma block size.
+ /// A value indicating whether the target plane is vertically subsampled.
+ /// The clipped height in 4x4 units of the target plane.
public int GetMaxBlockHigh(Av1BlockSize blockSize, bool subY)
{
int maxBlockHigh = blockSize.GetHeight();
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs
index 7e6978eda..e347ab95a 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs
@@ -5,70 +5,131 @@ using System;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Holds the coding decisions, buffers, and sequence context for one AV1 picture pass.
+///
internal class Av1PictureControlSet
{
+ ///
+ /// Gets or sets the partition neighbor contexts for each tile.
+ ///
public required Av1NeighborArrayUnit[] PartitionContexts { get; internal set; }
+ ///
+ /// Gets or sets the luma DC-sign and coefficient-level neighbor contexts for each tile.
+ ///
public required Av1NeighborArrayUnit[] LuminanceDcSignLevelCoefficientNeighbors { get; internal set; }
+ ///
+ /// Gets or sets the red-difference chroma DC-sign and coefficient-level neighbor contexts for each tile.
+ ///
public required Av1NeighborArrayUnit[] CrDcSignLevelCoefficientNeighbors { get; internal set; }
+ ///
+ /// Gets or sets the blue-difference chroma DC-sign and coefficient-level neighbor contexts for each tile.
+ ///
public required Av1NeighborArrayUnit[] CbDcSignLevelCoefficientNeighbors { get; internal set; }
+ ///
+ /// Gets or sets the transform-function neighbor contexts for each tile.
+ ///
public required Av1NeighborArrayUnit[] TransformFunctionContexts { get; internal set; }
+ ///
+ /// Gets or sets the sequence-wide encoder state.
+ ///
public required Av1SequenceControlSet Sequence { get; internal set; }
+ ///
+ /// Gets or sets the parent picture state shared across coding passes.
+ ///
public required Av1PictureParentControlSet Parent { get; internal set; }
+ ///
+ /// Gets or sets the frame segmentation identifiers used for spatial prediction.
+ ///
public required byte[] SegmentationNeighborMap { get; internal set; }
+ ///
+ /// Gets the frame grid that maps each 4x4 position to its mode-information span.
+ ///
public Av1ModeInfo[][] ModeInfoGrid { get; } = [];
+ ///
+ /// Gets or sets the contiguous mode-information storage addressed by .
+ ///
public required Av1ModeInfo[] Mip { get; internal set; }
+ ///
+ /// Gets or sets the row stride of in 4x4 mode-information units.
+ ///
public int ModeInfoStride { get; internal set; }
- // true if 4x4 blocks are disallowed for all frames, and NSQ is disabled (since granularity is
- // needed for 8x8 NSQ blocks). Used to compute the offset for mip.
+ ///
+ /// Gets or sets a value indicating whether the mode-information backing store uses 8x8 rather than 4x4 granularity.
+ ///
public bool Disallow4x4AllFrames { get; internal set; }
+ ///
+ /// Gets or sets the constrained directional enhancement filter presets for each filter block.
+ ///
public required int[][] CdefPreset { get; internal set; }
+ ///
+ /// Gets the mode-information span mapped to a frame position.
+ ///
+ /// The frame position in 4x4 mode-information units.
+ /// The mode-information span beginning at the position.
public Span GetFromModeInfoGrid(Point position)
=> this.ModeInfoGrid[(position.Y * this.ModeInfoStride) + position.X];
+ ///
+ /// Maps a frame position to the supplied mode-information span.
+ ///
+ /// The frame position in 4x4 mode-information units.
+ /// The mode-information entries to map.
public void SetModeInfoGridRow(Point position, Span span)
=> this.SetModeInfoGridRow((position.Y * this.ModeInfoStride) + position.X, span);
+ ///
+ /// Maps a linear grid offset to the supplied mode-information span.
+ ///
+ /// The linear grid offset.
+ /// The mode-information entries to map.
public void SetModeInfoGridRow(int offset, Span span)
{
+ // Grid entries own their arrays because the source span can refer to temporary traversal state.
this.ModeInfoGrid[offset] = new Av1ModeInfo[span.Length];
span.CopyTo(this.ModeInfoGrid[offset]);
}
///
- /// SVT: get_mbmi
+ /// Gets the macroblock mode information at a block origin and refreshes its grid mapping.
///
+ /// The block origin in 4x4 mode-information units.
+ /// The macroblock mode information at the origin.
internal Av1MacroBlockModeInfo GetMacroBlockModeInfo(Point blockOrigin)
{
int modeInfoStride = this.ModeInfoStride;
int offset = (blockOrigin.Y * modeInfoStride) + blockOrigin.X;
- // Reset the mi_grid (needs to be done here in case it was changed for NSQ blocks during MD - svt_aom_init_xd())
- // mip offset may be different from grid offset when 4x4 blocks are disallowed
+ // Rectangular mode-decision blocks can replace grid entries. Restore the entry from the
+ // contiguous backing store, whose index is halved when 4x4 blocks are globally disabled.
int disallow4x4 = this.Disallow4x4AllFrames ? 1 : 0;
int mipOffset = ((blockOrigin.Y >> disallow4x4) * (modeInfoStride >> disallow4x4)) + (blockOrigin.X >> disallow4x4);
this.SetModeInfoGridRow(offset, ((Span)this.Mip)[mipOffset..]);
- // use idx 0 as that's the first MacroBlockModeInfo in the block.
+ // The first mapped entry owns the macroblock state for the entire block.
Av1ModeInfo modeInfo = this.ModeInfoGrid[offset][0];
return modeInfo.MacroBlockModeInfo;
}
///
- /// SVT: svt_av1_update_segmentation_map
+ /// Writes a segment identifier to every segmentation-map entry covered by a block.
///
+ /// The block size.
+ /// The block origin in samples.
+ /// The segment identifier.
internal void UpdateSegmentation(Av1BlockSize blockSize, Point origin, int segmentId)
{
Av1EncoderCommon cm = this.Parent.Common;
@@ -80,6 +141,8 @@ internal class Av1PictureControlSet
int bh = blockSize.GetHeight();
int xmis = Math.Min(cm.ModeInfoColumnCount - mi_col, bw);
int ymis = Math.Min(cm.ModeInfoRowCount - mi_row, bh);
+
+ // Clip edge blocks to the coded mode-information grid before filling complete rows.
for (int y = 0; y < ymis; ++y)
{
int offset = mi_offset + (y * cm.ModeInfoColumnCount);
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureParentControlSet.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureParentControlSet.cs
index 2317f97f7..d3922de77 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureParentControlSet.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureParentControlSet.cs
@@ -5,19 +5,43 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Holds encoder state that is shared by all coding passes for one AV1 picture.
+///
internal class Av1PictureParentControlSet
{
+ ///
+ /// Gets or sets frame dimensions and tile state shared by encoder stages.
+ ///
public required Av1EncoderCommon Common { get; internal set; }
+ ///
+ /// Gets or sets the frame header being encoded.
+ ///
public required ObuFrameHeader FrameHeader { get; internal set; }
+ ///
+ /// Gets or sets the preceding quantizer index for each tile context.
+ ///
public required int[] PreviousQIndex { get; internal set; }
+ ///
+ /// Gets or sets the encoder palette-search level.
+ ///
public int PaletteLevel { get; internal set; }
+ ///
+ /// Gets or sets the frame width aligned for superblock traversal.
+ ///
public int AlignedWidth { get; internal set; }
+ ///
+ /// Gets or sets the frame height aligned for superblock traversal.
+ ///
public int AlignedHeight { get; internal set; }
+ ///
+ /// Gets or sets the geometry state for each superblock in the picture.
+ ///
public required Av1SuperblockGeometry[] SuperblockGeometry { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PlaneType.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PlaneType.cs
index 3c790f509..f6b70e542 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PlaneType.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PlaneType.cs
@@ -3,8 +3,18 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Identifies whether AV1 block processing targets the luma plane or either chroma plane.
+///
internal enum Av1PlaneType : int
{
+ ///
+ /// The luma plane.
+ ///
Y,
+
+ ///
+ /// Either chroma plane.
+ ///
Uv
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SequenceControlSet.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SequenceControlSet.cs
index e19b7ba7b..156badf07 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SequenceControlSet.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SequenceControlSet.cs
@@ -5,9 +5,18 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Holds encoder configuration and sequence-wide state shared by AV1 pictures.
+///
internal class Av1SequenceControlSet
{
+ ///
+ /// Gets or sets the sequence header that governs encoded pictures.
+ ///
public required ObuSequenceHeader SequenceHeader { get; internal set; }
+ ///
+ /// Gets or sets the maximum number of encoded blocks allocated for a picture.
+ ///
public int MaxBlockCount { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Superblock.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Superblock.cs
index a4d146595..780de9dd0 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Superblock.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1Superblock.cs
@@ -5,13 +5,28 @@ using static SixLabors.ImageSharp.Formats.Heif.Av1.Tiling.Av1TileWriter;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Holds encoder-side block decisions and transform data for one AV1 superblock.
+///
internal class Av1Superblock
{
+ ///
+ /// Gets or sets the final encoder decisions in partition traversal order.
+ ///
public required Av1EncoderBlockStruct[] FinalBlocks { get; set; }
+ ///
+ /// Gets or sets the tile containing the superblock.
+ ///
public required Av1TileInfo TileInfo { get; set; }
+ ///
+ /// Gets or sets the selected partition type for each partition-tree node.
+ ///
public required Av1PartitionType[] CodingUnitPartitionTypes { get; internal set; }
+ ///
+ /// Gets or sets the superblock index within the picture.
+ ///
public int Index { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockGeometry.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockGeometry.cs
index 6422b88cd..13e7f7df4 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockGeometry.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockGeometry.cs
@@ -3,7 +3,13 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Describes a superblock node's location, dimensions, and children in the encoder partition tree.
+///
internal class Av1SuperblockGeometry
{
+ ///
+ /// Gets or sets a value indicating whether the superblock lies completely within the coded frame.
+ ///
public bool IsComplete { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs
index 99a84cb3d..c381c2236 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1SuperblockInfo.cs
@@ -3,10 +3,21 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores the partition tree and decoded mode information for one AV1 superblock.
+///
internal class Av1SuperblockInfo
{
+ ///
+ /// Provides the frame-owned arrays addressed by this superblock view.
+ ///
private readonly Av1FrameInfo frameInfo;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The owning frame information.
+ /// The superblock position in the frame superblock grid.
public Av1SuperblockInfo(Av1FrameInfo frameInfo, Point position)
{
this.Position = position;
@@ -14,39 +25,82 @@ internal class Av1SuperblockInfo
}
///
- /// Gets the position of this superblock inside the tile, counted in superblocks.
+ /// Gets the position of this superblock in the frame superblock grid.
///
public Point Position { get; }
///
- /// Gets the position of this superblock inside the tile, counted in mode info blocks (of 4x4 pixels).
+ /// Gets the frame-relative superblock origin in 4x4 mode-information units.
///
public Point ModeInfoPosition => this.Position * this.frameInfo.SuperblockModeInfoSize;
+ ///
+ /// Gets a reference to the superblock quantizer-index delta.
+ ///
public ref int SuperblockDeltaQ => ref this.frameInfo.GetDeltaQuantizationIndex(this.Position);
+ ///
+ /// Gets the mode information that covers the superblock origin.
+ ///
public Av1BlockModeInfo SuperblockModeInfo => this.GetModeInfo(new Point(0, 0));
+ ///
+ /// Gets the luma coefficient storage reserved for this superblock.
+ ///
public Span CoefficientsY => this.frameInfo.GetCoefficientsY(this.Position);
+ ///
+ /// Gets the blue-difference chroma coefficient storage reserved for this superblock.
+ ///
public Span CoefficientsU => this.frameInfo.GetCoefficientsU(this.Position);
+ ///
+ /// Gets the red-difference chroma coefficient storage reserved for this superblock.
+ ///
public Span CoefficientsV => this.frameInfo.GetCoefficientsV(this.Position);
+ ///
+ /// Gets the constrained directional enhancement filter strengths for this superblock.
+ ///
public Span CdefStrength => this.frameInfo.GetCdefStrength(this.Position);
+ ///
+ /// Gets the loop-filter deltas for this superblock.
+ ///
public Span SuperblockDeltaLoopFilter => this.frameInfo.GetDeltaLoopFilter(this.Position);
+ ///
+ /// Gets or sets the next luma transform-information index while parsing this superblock.
+ ///
public int TransformInfoIndexY { get; internal set; }
+ ///
+ /// Gets or sets the next shared chroma transform-information index while parsing this superblock.
+ ///
public int TransformInfoIndexUv { get; internal set; }
+ ///
+ /// Gets or sets the number of mode-information records parsed for this superblock.
+ ///
public int BlockCount { get; internal set; }
+ ///
+ /// Gets the luma transform-information storage reserved for this superblock.
+ ///
+ /// The superblock luma transform-information span.
public Span GetTransformInfoY() => this.frameInfo.GetSuperblockTransformY(this.Position);
+ ///
+ /// Gets the shared chroma transform-information storage reserved for this superblock.
+ ///
+ /// The superblock chroma transform-information span.
public Span GetTransformInfoUv() => this.frameInfo.GetSuperblockTransformUv(this.Position);
+ ///
+ /// Gets the transform-information storage for the specified color plane.
+ ///
+ /// The zero-based color-plane index.
+ /// The transform-information span for the plane.
public Span GetTransformInfo(int plane) => this.frameInfo.GetSuperblockTransform(plane, this.Position);
///
@@ -54,10 +108,25 @@ internal class Av1SuperblockInfo
///
public Span GetModeInfos() => this.frameInfo.GetModeInfos(this.Position, this.BlockCount);
+ ///
+ /// Gets the mode information covering a position relative to this superblock.
+ ///
+ /// The position in 4x4 mode-information units relative to the superblock.
+ /// The mode information covering the position.
public Av1BlockModeInfo GetModeInfo(Point index) => this.frameInfo.GetModeInfo(this.Position, index);
+ ///
+ /// Gets the mode information covering a frame-relative position.
+ ///
+ /// The frame-relative position in 4x4 mode-information units.
+ /// The mode information covering the position.
public Av1BlockModeInfo GetModeInfoAt(Point index) => this.frameInfo.GetModeInfoAt(index);
+ ///
+ /// Gets the coefficient storage for the specified color plane.
+ ///
+ /// The color plane.
+ /// The coefficient span for the plane, or an empty span for an unsupported value.
public Span GetCoefficients(Av1Plane plane) => plane switch
{
Av1Plane.Y => this.CoefficientsY,
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileInfo.cs
index 52a6d0d70..b5cb18d5b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileInfo.cs
@@ -5,14 +5,27 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Describes one AV1 tile's superblock and mode-information boundaries.
+///
internal class Av1TileInfo
{
+ ///
+ /// Initializes a new instance of the class for the specified tile coordinates.
+ ///
+ /// The tile row index.
+ /// The tile column index.
+ /// The frame header that defines the tile layout.
public Av1TileInfo(int row, int column, ObuFrameHeader frameHeader)
{
this.SetTileRow(frameHeader.TilesInfo, frameHeader.ModeInfoRowCount, row);
this.SetTileColumn(frameHeader.TilesInfo, frameHeader.ModeInfoColumnCount, column);
}
+ ///
+ /// Initializes a new instance of the class by copying another tile description.
+ ///
+ /// The tile description to copy.
public Av1TileInfo(Av1TileInfo tileInfo)
{
this.ModeInfoColumnStart = tileInfo.ModeInfoColumnStart;
@@ -22,16 +35,37 @@ internal class Av1TileInfo
this.TileIndex = tileInfo.TileIndex;
}
+ ///
+ /// Gets the first mode-information row in the tile.
+ ///
public int ModeInfoRowStart { get; private set; }
+ ///
+ /// Gets the exclusive end mode-information row in the tile.
+ ///
public int ModeInfoRowEnd { get; private set; }
+ ///
+ /// Gets the first mode-information column in the tile.
+ ///
public int ModeInfoColumnStart { get; private set; }
+ ///
+ /// Gets the exclusive end mode-information column in the tile.
+ ///
public int ModeInfoColumnEnd { get; private set; }
+ ///
+ /// Gets the tile column and row indices.
+ ///
public Point TileIndex { get; private set; }
+ ///
+ /// Selects the tile row and updates its mode-information boundaries.
+ ///
+ /// The tile layout.
+ /// The coded frame height in mode-information rows.
+ /// The tile row index.
public void SetTileRow(ObuTileGroupHeader tileGroupHeader, int modeInfoRowCount, int row)
{
this.ModeInfoRowStart = tileGroupHeader.TileRowStartModeInfo[row];
@@ -41,6 +75,12 @@ internal class Av1TileInfo
this.TileIndex = loc;
}
+ ///
+ /// Selects the tile column and updates its mode-information boundaries.
+ ///
+ /// The tile layout.
+ /// The coded frame width in mode-information columns.
+ /// The tile column index.
public void SetTileColumn(ObuTileGroupHeader tileGroupHeader, int modeInfoColumnCount, int column)
{
this.ModeInfoColumnStart = tileGroupHeader.TileColumnStartModeInfo[column];
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
index cd34a8777..ac7c89309 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileReader.cs
@@ -11,37 +11,107 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Parses partition, mode, transform, and coefficient syntax for one AV1 tile.
+///
internal class Av1TileReader : IAv1TileReader
{
+ ///
+ /// The default self-guided restoration projection coefficients for each color plane.
+ ///
private static readonly int[] SgrprojXqdMid = [-32, 31];
+
+ ///
+ /// The default Wiener restoration taps retained between restoration units.
+ ///
private static readonly int[] WienerTapsMid = [3, -7, 15];
+
+ ///
+ /// Maps packed coefficient sign classes to their signed contribution to the DC context.
+ ///
private static readonly int[] Signs = [0, -1, 1];
+
+ ///
+ /// Maps the summed neighboring DC signs to the AV1 DC-sign entropy context.
+ ///
private static readonly int[] DcSignContexts = [
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2];
+ ///
+ /// Maps the minimum and union of luma neighbor levels to a transform-block skip context.
+ ///
private static readonly int[][] SkipContexts = [
[1, 2, 2, 2, 3], [1, 4, 4, 4, 5], [1, 4, 4, 4, 5], [1, 4, 4, 4, 5], [1, 4, 4, 4, 6]];
+ ///
+ /// Stores the preceding self-guided restoration coefficients for each color plane.
+ ///
private int[][] referenceSgrXqd = [];
+
+ ///
+ /// Stores the preceding horizontal and vertical Wiener taps for each color plane.
+ ///
private int[][][] referenceLrWiener = [];
+
+ ///
+ /// Tracks entropy, partition, transform, and palette state above the current block.
+ ///
private readonly Av1ParseAboveNeighbor4x4Context aboveNeighborContext;
+
+ ///
+ /// Tracks entropy, partition, transform, and palette state left of the current block.
+ ///
private readonly Av1ParseLeftNeighbor4x4Context leftNeighborContext;
+
+ ///
+ /// The quantizer index carried between delta-quantized blocks in the current tile.
+ ///
private int currentQuantizerIndex;
+
+ ///
+ /// Stores the segment identifier covering each 4x4 frame position.
+ ///
private readonly int[][] segmentIds = [];
+
+ ///
+ /// Stores per-plane transform counts for each forced 64x64 residual region.
+ ///
private readonly int[][] transformUnitCount;
+
+ ///
+ /// Tracks the first unassigned transform-information index for luma and shared chroma storage.
+ ///
private readonly int[] firstTransformOffset = new int[2];
+
+ ///
+ /// Tracks the next coefficient slot for each color plane within the current superblock.
+ ///
private readonly int[] coefficientIndex = [];
+
+ ///
+ /// Provides allocator and decoder configuration to tile entropy decoding.
+ ///
private readonly Configuration configuration;
+
+ ///
+ /// Reconstructs each parsed superblock when pixel decoding is requested; otherwise, tile parsing is metadata-only.
+ ///
private readonly IAv1FrameDecoder? frameDecoder;
+ ///
+ /// Initializes a new instance of the class for syntax parsing without reconstruction.
+ ///
+ /// The decoder configuration.
+ /// The active AV1 sequence header.
+ /// The frame header whose tiles will be parsed.
public Av1TileReader(Configuration configuration, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader)
{
this.FrameHeader = frameHeader;
this.configuration = configuration;
this.SequenceHeader = sequenceHeader;
- // init_main_frame_ctxt
+ // FrameInfo owns all traversal-order records and coefficient storage produced by the tile readers.
this.FrameInfo = new(this.SequenceHeader);
this.segmentIds = new int[this.FrameHeader.ModeInfoRowCount][];
for (int y = 0; y < this.FrameHeader.ModeInfoRowCount; y++)
@@ -49,8 +119,7 @@ internal class Av1TileReader : IAv1TileReader
this.segmentIds[y] = new int[this.FrameHeader.ModeInfoColumnCount];
}
- // reallocate_parse_context_memory
- // Hard code number of threads to 1 for now.
+ // Above contexts span the aligned frame width, while left contexts are reused for each superblock row.
int planesCount = sequenceHeader.ColorConfig.PlaneCount;
int superblockColumnCount =
Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, sequenceHeader.SuperblockSizeLog2) >> sequenceHeader.SuperblockSizeLog2;
@@ -65,19 +134,38 @@ internal class Av1TileReader : IAv1TileReader
this.coefficientIndex = new int[Av1Constants.MaxPlanes];
}
+ ///
+ /// Initializes a new instance of the class that reconstructs parsed superblocks.
+ ///
+ /// The decoder configuration.
+ /// The active AV1 sequence header.
+ /// The frame header whose tiles will be parsed.
+ /// The frame decoder that reconstructs each parsed superblock.
public Av1TileReader(Configuration configuration, ObuSequenceHeader sequenceHeader, ObuFrameHeader frameHeader, IAv1FrameDecoder frameDecoder)
: this(configuration, sequenceHeader, frameHeader)
=> this.frameDecoder = frameDecoder;
+ ///
+ /// Gets the frame header whose tile syntax is being parsed.
+ ///
public ObuFrameHeader FrameHeader { get; }
+ ///
+ /// Gets the sequence header governing the frame.
+ ///
public ObuSequenceHeader SequenceHeader { get; }
+ ///
+ /// Gets the frame-owned mode, transform, coefficient, quantizer, and filter state populated by tile parsing.
+ ///
public Av1FrameInfo FrameInfo { get; }
///
- /// SVT: parse_tile
+ /// Parses one tile's partition, mode, transform, coefficient, and filter syntax in superblock order.
///
+ /// The entropy-coded tile payload.
+ /// The zero-based tile index in row-major order.
+ /// Corresponds to parse_tile in SVT-AV1.
public void ReadTile(Span tileData, int tileNum)
{
Av1SymbolDecoder reader = new(this.configuration, tileData, this.FrameHeader.QuantizationParameters.BaseQIndex);
@@ -92,7 +180,7 @@ internal class Av1TileReader : IAv1TileReader
this.ClearLoopFilterDelta();
int planesCount = this.SequenceHeader.ColorConfig.PlaneCount;
- // Default initialization of Wiener and SGR Filter.
+ // Restoration coefficients are differentially coded, so each tile begins from the AV1 defaults.
this.referenceSgrXqd = new int[planesCount][];
this.referenceLrWiener = new int[planesCount][][];
for (int plane = 0; plane < planesCount; plane++)
@@ -129,15 +217,24 @@ internal class Av1TileReader : IAv1TileReader
this.ReadLoopRestoration(modeInfoPosition, superBlockSize);
this.ParsePartition(ref reader, modeInfoPosition, superBlockSize, superblockInfo, tileInfo);
- // decoding of the superblock
+ // Identify-only parsing omits a frame decoder but still populates the complete syntax model.
this.frameDecoder?.DecodeSuperblock(modeInfoPosition, superblockInfo, tileInfo);
}
}
}
+ ///
+ /// Resets all frame loop-filter delta state before parsing a tile.
+ ///
private void ClearLoopFilterDelta()
=> this.FrameInfo.ClearDeltaLoopFilter();
+ ///
+ /// Reads loop-restoration unit syntax that begins at a superblock location.
+ ///
+ /// The superblock origin in 4x4 mode-information units.
+ /// The superblock size.
+ /// A color plane signals a loop-restoration filter.
private void ReadLoopRestoration(Point modeInfoLocation, Av1BlockSize superBlockSize)
{
int planesCount = this.SequenceHeader.ColorConfig.PlaneCount;
@@ -145,15 +242,20 @@ internal class Av1TileReader : IAv1TileReader
{
if (this.FrameHeader.LoopRestorationParameters.Items[plane].Type != ObuRestorationType.None)
{
- // TODO: Implement.
throw new NotImplementedException("No loop restoration filter support.");
}
}
}
///
- /// 5.11.4. Decode partition syntax.
+ /// Decodes AV1 partition syntax and recursively visits each resulting coding block.
///
+ /// The tile symbol decoder.
+ /// The parent block origin in 4x4 mode-information units.
+ /// The parent block size.
+ /// The containing superblock.
+ /// The active tile boundaries.
+ /// Implements AV1 section 5.11.4.
private void ParsePartition(ref Av1SymbolDecoder reader, Point modeInfoLocation, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo)
{
int columnIndex = modeInfoLocation.X;
@@ -189,6 +291,9 @@ internal class Av1TileReader : IAv1TileReader
Av1BlockSize subSize = partitionType.GetBlockSubSize(blockSize);
Av1BlockSize splitSize = Av1PartitionType.Split.GetBlockSubSize(blockSize);
+
+ // Partition syntax is depth-first. The visit order here is also the order in which mode,
+ // transform, and coefficient records are appended to their frame-owned arrays.
switch (partitionType)
{
case Av1PartitionType.Split:
@@ -284,6 +389,15 @@ internal class Av1TileReader : IAv1TileReader
this.UpdatePartitionContext(new Point(columnIndex, rowIndex), tileInfo, superblockInfo, subSize, blockSize, partitionType);
}
+ ///
+ /// Parses all syntax associated with one final coding block and stores its frame mode information.
+ ///
+ /// The tile symbol decoder.
+ /// The block origin in 4x4 mode-information units.
+ /// The final block size.
+ /// The containing superblock.
+ /// The active tile boundaries.
+ /// The partition type that produced the block.
private void ParseBlock(ref Av1SymbolDecoder reader, Point modeInfoLocation, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo, Av1PartitionType partitionType)
{
int rowIndex = modeInfoLocation.Y;
@@ -328,13 +442,16 @@ internal class Av1TileReader : IAv1TileReader
this.Residual(ref reader, partitionInfo, superblockInfo, tileInfo, blockSize);
- // Update the Frame buffer for this ModeInfo.
+ // Store the record only after all syntax has populated it, then map every covered 4x4 position.
this.FrameInfo.UpdateModeInfo(blockModeInfo, superblockInfo);
}
///
- /// SVT: reset_skip_context
+ /// Clears coefficient neighbor contexts across every plane of a skipped block.
///
+ /// The skipped block and its frame position.
+ /// The active tile boundaries.
+ /// Corresponds to reset_skip_context in SVT-AV1.
private void ResetSkipContext(Av1PartitionInfo partitionInfo, Av1TileInfo tileInfo)
{
int planesCount = this.SequenceHeader.ColorConfig.PlaneCount;
@@ -354,9 +471,14 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.34. Residual syntax.
+ /// Parses every luma and chroma transform block and its coefficients for a coding block.
///
- /// SVT: parse_residual
+ /// The tile symbol decoder.
+ /// The current coding block.
+ /// The containing superblock and coefficient storage.
+ /// The active tile boundaries.
+ /// The coding block size.
+ /// Implements AV1 section 5.11.34 and corresponds to parse_residual in SVT-AV1.
private void Residual(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo, Av1BlockSize blockSize)
{
int maxBlocksWide = partitionInfo.GetMaxBlockWide(blockSize, false);
@@ -378,6 +500,8 @@ internal class Av1TileReader : IAv1TileReader
transformInfoIndices[2] = transformInfoIndices[1] + chromaTransformUnitCount;
int forceSplitCount = 0;
+ // AV1 forces residual traversal into at most 64x64 regions even when the coding block is larger.
+ // transformUnitCount preserves the transform geometry generated for each such region and plane.
for (int row = 0; row < maxBlocksHigh; row += modeUnitBlocksHigh)
{
for (int column = 0; column < maxBlocksWide; column += modeUnitBlocksWide)
@@ -397,7 +521,8 @@ internal class Av1TileReader : IAv1TileReader
Span transformInfoSpan = (plane == 0) ? superblockInfo.GetTransformInfoY() : superblockInfo.GetTransformInfoUv();
if (isLosslessBlock)
{
- // TODO: Implement.
+ // Lossless coding fixes transforms at 4x4, so count each clipped 4x4 unit
+ // directly after applying the plane's chroma subsampling.
int unitHeight = Av1Math.RoundPowerOf2(Math.Min(modeUnitBlocksHigh + row, maxBlocksHigh), 0);
int unitWidth = Av1Math.RoundPowerOf2(Math.Min(modeUnitBlocksWide + column, maxBlocksWide), 0);
DebugGuard.IsTrue(transformInfoSpan[transformInfoIndices[plane]].Size == Av1TransformSize.Size4x4, "Lossless frame shall have transform units of size 4x4.");
@@ -445,6 +570,8 @@ internal class Av1TileReader : IAv1TileReader
if (endOfBlock != 0)
{
+ // Coefficients are stored as an end index followed by scan-order values, so the
+ // next transform begins after both the prefix and its decoded coefficient range.
this.coefficientIndex[plane] += endOfBlock + 1;
transformInfo.CodeBlockFlag = true;
}
@@ -462,6 +589,13 @@ internal class Av1TileReader : IAv1TileReader
}
}
+ ///
+ /// Determines whether a luma coding block owns chroma mode and residual syntax at its frame position.
+ ///
+ /// The sequence header describing chroma subsampling.
+ /// The block origin in 4x4 luma mode-information units.
+ /// The luma block size.
+ /// when the block is a chroma reference position; otherwise, .
public static bool HasChroma(ObuSequenceHeader sequenceHeader, Point modeInfoLocation, Av1BlockSize blockSize)
{
int blockWide = blockSize.Get4x4WideCount();
@@ -474,10 +608,23 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.35. Transform block syntax.
+ /// Derives a transform block's entropy context and decodes its coefficient syntax.
///
+ /// The tile symbol decoder.
+ /// The containing coding block.
+ /// The destination beginning at this transform's coefficient slot.
+ /// The transform geometry and syntax state to populate.
+ /// The zero-based color-plane index.
+ /// The transform's horizontal offset within the coding block in 4x4 units.
+ /// The transform's vertical offset within the coding block in 4x4 units.
+ /// The frame-relative transform column in 4x4 units of the target plane.
+ /// The frame-relative transform row in 4x4 units of the target plane.
+ /// The transform size.
+ /// A value indicating whether the target plane is horizontally subsampled.
+ /// A value indicating whether the target plane is vertically subsampled.
+ /// The decoded end-of-block coefficient position, or zero for an all-zero transform.
///
- /// The implementation is taken from SVT-AV1 library, which deviates from the code flow in the specification.
+ /// Implements AV1 section 5.11.35 using the traversal shape of the corresponding SVT-AV1 implementation.
///
private int ParseTransformBlock(
ref Av1SymbolDecoder reader,
@@ -517,10 +664,22 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.39. Coefficients syntax.
+ /// Decodes transform coefficients and updates the coefficient neighbor contexts for one color plane.
///
+ /// The tile symbol decoder.
+ /// The containing coding block.
+ /// The frame-relative transform row in 4x4 units of the target plane.
+ /// The frame-relative transform column in 4x4 units of the target plane.
+ /// The horizontal transform offset within the coding block in 4x4 units.
+ /// The vertical transform offset within the coding block in 4x4 units.
+ /// The zero-based color-plane index.
+ /// The coefficient skip and DC-sign entropy contexts.
+ /// The transform size.
+ /// The transform syntax state to populate.
+ /// The destination beginning at this transform's coefficient slot.
+ /// The decoded end-of-block coefficient position, or zero for an all-zero transform.
///
- /// The implementation is taken from SVT-AV1 library, which deviates from the code flow in the specification.
+ /// Implements AV1 section 5.11.39 using the traversal shape of the corresponding SVT-AV1 implementation.
///
private int ParseCoefficients(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo, int blockRow, int blockColumn, int aboveOffset, int leftOffset, int plane, Av1TransformBlockContext transformBlockContext, Av1TransformSize transformSize, Av1TransformInfo transformInfo, Span coefficientBuffer)
{
@@ -539,6 +698,17 @@ internal class Av1TileReader : IAv1TileReader
return reader.ReadCoefficients(partitionInfo.ModeInfo, blockPosition, this.aboveNeighborContext.GetContext(plane), this.leftNeighborContext.GetContext(plane), aboveOffset, leftOffset, plane, blocksWide, blocksHigh, transformBlockContext, transformSize, isLossless, this.FrameHeader.UseReducedTransformSet, transformInfo, partitionInfo.ModeBlockToRightEdge, partitionInfo.ModeBlockToBottomEdge, coefficientBuffer);
}
+ ///
+ /// Derives coefficient skip and DC-sign contexts from the transform block's above and left neighbors.
+ ///
+ /// The transform size.
+ /// The zero-based color-plane index.
+ /// The containing block size on the target plane.
+ /// The transform height clipped to the frame in 4x4 units.
+ /// The transform width clipped to the frame in 4x4 units.
+ /// The frame-relative transform row in 4x4 units of the target plane.
+ /// The frame-relative transform column in 4x4 units of the target plane.
+ /// The derived transform-block entropy contexts.
private Av1TransformBlockContext GetTransformBlockContext(Av1TransformSize transformSize, int plane, Av1BlockSize planeBlockSize, int transformBlockUnitHighCount, int transformBlockUnitWideCount, int startY, int startX)
{
Av1TransformBlockContext transformBlockContext = new();
@@ -548,6 +718,8 @@ internal class Av1TileReader : IAv1TileReader
int k = 0;
int mask = (1 << Av1Constants.CoefficientContextBitCount) - 1;
+ // The high bits of each neighbor value encode its DC sign class. Summing both edges maps
+ // negative, balanced, and positive neighborhoods to the AV1 DC-sign context.
do
{
uint sign = (uint)aboveContext[k] >> Av1Constants.CoefficientContextBitCount;
@@ -575,6 +747,7 @@ internal class Av1TileReader : IAv1TileReader
}
else
{
+ // Luma skip contexts preserve both the weakest neighboring level and whether either edge is stronger.
int top = 0;
int left = 0;
@@ -602,6 +775,8 @@ internal class Av1TileReader : IAv1TileReader
}
else
{
+ // Chroma needs only the presence of nonzero levels on each edge, plus an offset that
+ // distinguishes a transform smaller than its containing plane block.
int contextBase = GetEntropyContext(transformSize, aboveContext, leftContext);
int contextOffset = planeBlockSize.GetPelsLog2Count() > transformSize.ToBlockSize().GetPelsLog2Count() ? 10 : 7;
transformBlockContext.SkipContext = contextBase + contextOffset;
@@ -610,11 +785,20 @@ internal class Av1TileReader : IAv1TileReader
return transformBlockContext;
}
+ ///
+ /// Determines whether the above and left edges contain nonzero chroma coefficient contexts.
+ ///
+ /// The transform size that selects how many edge entries to inspect.
+ /// The above coefficient contexts.
+ /// The left coefficient contexts.
+ /// The sum of the nonzero-above and nonzero-left flags.
private static int GetEntropyContext(Av1TransformSize transformSize, int[] above, int[] left)
{
bool aboveEntropyContext = false;
bool leftEntropyContext = false;
+ // The reference implementation tests packed 16, 32, 64, or 128-bit edge groups. Enumerating
+ // each transform shape keeps those exact edge widths without unaligned native memory reads.
switch (transformSize)
{
case Av1TransformSize.Size4x4:
@@ -742,8 +926,15 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.15. TX size syntax.
+ /// Selects the transform size for a coding block from lossless, explicit-selection, or maximum-size rules.
///
+ /// The tile symbol decoder.
+ /// The current coding block.
+ /// The containing superblock.
+ /// The active tile boundaries.
+ /// A value indicating whether transform-size selection syntax is allowed at this node.
+ /// The selected transform size.
+ /// Implements AV1 section 5.11.15.
private Av1TransformSize ReadTransformSize(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo, bool allowSelect)
{
Av1BlockModeInfo modeInfo = partitionInfo.ModeInfo;
@@ -760,6 +951,14 @@ internal class Av1TileReader : IAv1TileReader
return modeInfo.BlockSize.GetMaximumTransformSize();
}
+ ///
+ /// Reads a transform size using the available above and left transform-size contexts.
+ ///
+ /// The tile symbol decoder.
+ /// The current coding block.
+ /// The containing superblock.
+ /// The active tile boundaries.
+ /// The decoded transform size.
private Av1TransformSize ReadSelectedTransformSize(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo)
{
int context = 0;
@@ -792,22 +991,34 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// Section 5.11.16. Block TX size syntax.
+ /// Reads a coding block's transform size, updates neighbor contexts, and creates its transform geometry records.
///
- /// SVT: read_block_tx_size
+ /// The tile symbol decoder.
+ /// The block origin in 4x4 mode-information units.
+ /// The current coding block.
+ /// The containing superblock.
+ /// The active tile boundaries.
+ /// Implements AV1 section 5.11.16 and corresponds to read_block_tx_size in SVT-AV1.
private void ReadBlockTransformSize(ref Av1SymbolDecoder reader, Point modeInfoLocation, Av1PartitionInfo partitionInfo, Av1SuperblockInfo superblockInfo, Av1TileInfo tileInfo)
{
Av1BlockSize blockSize = partitionInfo.ModeInfo.BlockSize;
int block4x4Width = blockSize.Get4x4WideCount();
int block4x4Height = blockSize.Get4x4HighCount();
- // First condition in spec is for INTER frames, implemented only the INTRA condition.
+ // HEIF still-image decoding follows the independently decodable intra-frame transform-size branch.
Av1TransformSize transformSize = this.ReadTransformSize(ref reader, partitionInfo, superblockInfo, tileInfo, true);
this.aboveNeighborContext.UpdateTransformation(modeInfoLocation, tileInfo, transformSize, blockSize, false);
this.leftNeighborContext.UpdateTransformation(modeInfoLocation, superblockInfo, transformSize, blockSize, false);
this.UpdateTransformInfo(partitionInfo, superblockInfo, blockSize, transformSize);
}
+ ///
+ /// Populates luma and chroma transform-information records in residual traversal order.
+ ///
+ /// The current coding block.
+ /// The containing superblock and transform storage.
+ /// The coding block size.
+ /// The selected luma transform size.
private unsafe void UpdateTransformInfo(Av1PartitionInfo partitionInfo, Av1SuperblockInfo superblockInfo, Av1BlockSize blockSize, Av1TransformSize transformSize)
{
int transformInfoYIndex = partitionInfo.ModeInfo.FirstTransformLocation[(int)Av1PlaneType.Y];
@@ -829,6 +1040,8 @@ internal class Av1TileReader : IAv1TileReader
bool isLossLess = this.FrameHeader.LosslessArray[partitionInfo.ModeInfo.SegmentId];
Av1TransformSize transformSizeUv = isLossLess ? Av1TransformSize.Size4x4 : blockSize.GetMaxUvTransformSize(subX, subY);
+ // Residual syntax visits at most 64x64 luma regions. Record transform geometry in the same
+ // nested region/row/column order so coefficient parsing and reconstruction consume matching spans.
for (int idy = 0; idy < maxBlockHigh; idy += height)
{
for (int idx = 0; idx < maxBlockWide; idx += width, forceSplitCount++)
@@ -836,7 +1049,7 @@ internal class Av1TileReader : IAv1TileReader
int lumaTransformUnitCount = 0;
int chromaTransformUnitCount = 0;
- // Update Luminance Transform Info.
+ // Luma transform offsets remain relative to the coding block in 4x4 luma units.
int stepColumn = transformSize.Get4x4WideCount();
int stepRow = transformSize.Get4x4HighCount();
@@ -861,7 +1074,7 @@ internal class Av1TileReader : IAv1TileReader
continue;
}
- // Update Chroma Transform Info.
+ // Chroma geometry is rounded to the subsampling grid before stepping its transform size.
stepColumn = transformSizeUv.Get4x4WideCount();
stepRow = transformSizeUv.Get4x4HighCount();
@@ -884,7 +1097,7 @@ internal class Av1TileReader : IAv1TileReader
}
}
- // Cr Transform Info Update from Cb.
+ // U and V share transform geometry, so append a second copy for V after the complete U sequence.
if (totalChromaTransformUnitCount != 0)
{
DebugGuard.IsTrue(
@@ -911,26 +1124,31 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.49. Palette tokens syntax.
+ /// Reads luma and chroma palette-map tokens when a block selects palette prediction.
///
+ /// The tile symbol decoder.
+ /// The current coding block.
+ /// The block selects a nonempty luma or chroma palette.
+ /// Implements AV1 section 5.11.49.
private static void ReadPaletteTokens(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) != 0)
{
- // TODO: Implement.
throw new NotImplementedException();
}
if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Uv) != 0)
{
- // TODO: Implement.
throw new NotImplementedException();
}
}
///
- /// 5.11.6. Mode info syntax.
+ /// Reads the prediction, segmentation, skip, quantizer, and filter mode information for a still-image block.
///
+ /// The tile symbol decoder.
+ /// The current coding block.
+ /// Implements the intra-frame branch of AV1 section 5.11.6.
private void ReadModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
DebugGuard.IsTrue(this.FrameHeader.FrameType is ObuFrameType.KeyFrame or ObuFrameType.IntraOnlyFrame, "Only INTRA frames supported.");
@@ -938,8 +1156,11 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.7. Intra frame mode info syntax.
+ /// Reads all intra-frame mode syntax for a coding block in bitstream order.
///
+ /// The tile symbol decoder.
+ /// The current coding block and its neighbors.
+ /// Implements AV1 section 5.11.7.
private void ReadIntraFrameModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
if (this.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip)
@@ -947,7 +1168,6 @@ internal class Av1TileReader : IAv1TileReader
this.IntraSegmentId(ref reader, partitionInfo);
}
- // this.skipMode = false;
partitionInfo.ModeInfo.Skip = this.ReadSkip(ref reader, partitionInfo);
if (!this.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip)
{
@@ -962,8 +1182,9 @@ internal class Av1TileReader : IAv1TileReader
this.ReadDeltaLoopFilter(ref reader, partitionInfo);
}
- partitionInfo.ReferenceFrame[0] = 0; // IntraFrame;
- partitionInfo.ReferenceFrame[1] = -1; // None;
+ // Independently decodable still-image blocks reference only the current intra frame.
+ partitionInfo.ReferenceFrame[0] = 0;
+ partitionInfo.ReferenceFrame[1] = -1;
partitionInfo.ModeInfo.SetPaletteSizes(0, 0);
bool useIntraBlockCopy = false;
if (this.AllowIntraBlockCopy())
@@ -978,10 +1199,8 @@ internal class Av1TileReader : IAv1TileReader
}
else
{
- // this.IsInter = false;
partitionInfo.ModeInfo.YMode = reader.ReadYMode(partitionInfo.AboveModeInfo, partitionInfo.LeftModeInfo);
- // 5.11.42.Intra angle info luma syntax.
partitionInfo.ModeInfo.AngleDelta[(int)Av1PlaneType.Y] = IntraAngleInfo(ref reader, partitionInfo.ModeInfo.YMode, partitionInfo.ModeInfo.BlockSize);
if (partitionInfo.IsChroma && !this.SequenceHeader.ColorConfig.IsMonochrome)
{
@@ -991,7 +1210,6 @@ internal class Av1TileReader : IAv1TileReader
ReadChromaFromLumaAlphas(ref reader, partitionInfo.ModeInfo);
}
- // 5.11.43.Intra angle info chroma syntax.
partitionInfo.ModeInfo.AngleDelta[(int)Av1PlaneType.Uv] = IntraAngleInfo(ref reader, partitionInfo.ModeInfo.UvMode, partitionInfo.ModeInfo.BlockSize);
}
else
@@ -1011,27 +1229,41 @@ internal class Av1TileReader : IAv1TileReader
}
}
+ ///
+ /// Determines whether the frame header permits intra block copy for an intra still image.
+ ///
+ /// when the frame and sequence enable intra block copy; otherwise, .
private bool AllowIntraBlockCopy()
=> (this.FrameHeader.FrameType is ObuFrameType.KeyFrame or ObuFrameType.IntraOnlyFrame) &&
(this.SequenceHeader.ForceScreenContentTools > 0) &&
this.FrameHeader.AllowIntraBlockCopy;
+ ///
+ /// Determines whether chroma-from-luma prediction is available for a coding block.
+ ///
+ /// The current coding block.
+ /// when the lossless transform or block dimensions permit chroma-from-luma prediction; otherwise, .
private bool IsChromaForLumaAllowed(Av1PartitionInfo partitionInfo)
{
if (this.FrameHeader.LosslessArray[partitionInfo.ModeInfo.SegmentId])
{
- // In lossless, CfL is available when the partition size is equal to the
- // transform size.
+ // Lossless mode fixes transforms at 4x4, so CfL is available only when the subsampled
+ // plane block is itself 4x4 and therefore has no smaller transform partition.
bool subX = this.SequenceHeader.ColorConfig.SubSamplingX;
bool subY = this.SequenceHeader.ColorConfig.SubSamplingY;
Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY);
return planeBlockSize == Av1BlockSize.Block4x4;
}
- // Spec: CfL is available to luma partitions lesser than or equal to 32x32
+ // Outside lossless mode, AV1 limits CfL to luma blocks no larger than 32x32.
return partitionInfo.ModeInfo.BlockSize.GetWidth() <= 32 && partitionInfo.ModeInfo.BlockSize.GetHeight() <= 32;
}
+ ///
+ /// Reads filter-intra selection for an eligible DC-predicted luma block.
+ ///
+ /// The tile symbol decoder.
+ /// The current coding block.
private void FilterIntraModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = false;
@@ -1050,16 +1282,21 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.46. Palette mode info syntax.
+ /// Reads palette size and color syntax for an eligible screen-content block.
///
- private void PaletteModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) =>
-
- // TODO: Implement.
- throw new NotImplementedException();
+ /// The tile symbol decoder.
+ /// The current coding block.
+ /// Palette-mode syntax is not implemented.
+ /// Implements AV1 section 5.11.46.
+ private void PaletteModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
+ => throw new NotImplementedException();
///
- /// 5.11.45. Read CFL alphas syntax.
+ /// Reads the joint signs and nonzero alpha magnitudes for chroma-from-luma prediction.
///
+ /// The tile symbol decoder.
+ /// The block mode information to populate.
+ /// Implements AV1 section 5.11.45.
private static void ReadChromaFromLumaAlphas(ref Av1SymbolDecoder reader, Av1BlockModeInfo modeInfo)
{
int jointSignPlus1 = reader.ReadChromFromLumaSign() + 1;
@@ -1079,8 +1316,13 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.42. and 5.11.43.
+ /// Reads a directional intra-prediction angle adjustment when the block and mode permit one.
///
+ /// The tile symbol decoder.
+ /// The selected luma or chroma prediction mode.
+ /// The block size.
+ /// The signed angle adjustment.
+ /// Implements AV1 sections 5.11.42 and 5.11.43.
private static int IntraAngleInfo(ref Av1SymbolDecoder reader, Av1PredictionMode mode, Av1BlockSize blockSize)
{
int angleDelta = 0;
@@ -1093,12 +1335,20 @@ internal class Av1TileReader : IAv1TileReader
return angleDelta;
}
+ ///
+ /// Determines whether a prediction mode belongs to the AV1 directional-mode range.
+ ///
+ /// The prediction mode.
+ /// for a directional mode; otherwise, .
private static bool IsDirectionalMode(Av1PredictionMode mode)
=> mode is >= Av1PredictionMode.Vertical and <= Av1PredictionMode.Directional67Degrees;
///
- /// 5.11.8. Intra segment ID syntax.
+ /// Reads or inherits a segment identifier and writes it over every 4x4 position covered by the block.
///
+ /// The tile symbol decoder.
+ /// The current coding block.
+ /// Implements AV1 section 5.11.8.
private void IntraSegmentId(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
if (this.FrameHeader.SegmentationParameters.Enabled)
@@ -1111,6 +1361,8 @@ internal class Av1TileReader : IAv1TileReader
int modeInfoCountX = Math.Min(this.FrameHeader.ModeInfoColumnCount - partitionInfo.ColumnIndex, blockWidth4x4);
int modeInfoCountY = Math.Min(this.FrameHeader.ModeInfoRowCount - partitionInfo.RowIndex, blockHeight4x4);
int segmentId = partitionInfo.ModeInfo.SegmentId;
+
+ // Later blocks predict from 4x4 positions, so replicate one block ID over its clipped frame coverage.
for (int y = 0; y < modeInfoCountY; y++)
{
int[] segmentRow = this.segmentIds[partitionInfo.RowIndex + y];
@@ -1122,8 +1374,11 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.9. Read segment ID syntax.
+ /// Predicts and, when required, decodes the segment identifier for an intra block.
///
+ /// The tile symbol decoder.
+ /// The current coding block and its available neighbors.
+ /// Implements AV1 section 5.11.9.
private void ReadSegmentId(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
int predictor;
@@ -1166,7 +1421,9 @@ internal class Av1TileReader : IAv1TileReader
}
else
{
- int ctx = prevUL < 0 ? 0 /* Edge cases */
+ // Any unavailable neighbor selects the edge context; otherwise, agreement among two
+ // or three neighbors increases the specificity of the segment-ID distribution.
+ int ctx = prevUL < 0 ? 0
: prevUL == prevU && prevUL == prevL ? 2
: prevUL == prevU || prevUL == prevL || prevU == prevL ? 1 : 0;
int lastActiveSegmentId = this.FrameHeader.SegmentationParameters.LastActiveSegmentId;
@@ -1175,9 +1432,11 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// 5.11.56. Read CDEF syntax.
+ /// Reads the constrained directional enhancement filter strength for the block's 64x64 filter unit.
///
- /// SVT: read_cdef
+ /// The tile symbol decoder.
+ /// The current coding block.
+ /// Implements AV1 section 5.11.56 and corresponds to read_cdef in SVT-AV1.
private void ReadCdef(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
if (partitionInfo.ModeInfo.Skip || this.FrameHeader.CodedLossless || !this.SequenceHeader.EnableCdef || this.FrameHeader.AllowIntraBlockCopy)
@@ -1194,7 +1453,8 @@ internal class Av1TileReader : IAv1TileReader
int cdfStrength = reader.ReadCdfStrength(this.FrameHeader.CdefParameters.BitCount);
partitionInfo.CdefStrength[index] = cdfStrength;
- // Populate to nearby 64x64s if needed based on h4 & w4
+ // A block in a 128x128 superblock can cover multiple 64x64 CDEF units. Replicate the
+ // first decoded strength so subsequent blocks in every covered unit observe it as assigned.
if (this.SequenceHeader.SuperblockSize == Av1BlockSize.Block128x128)
{
int w4 = partitionInfo.ModeInfo.BlockSize.Get4x4WideCount();
@@ -1210,6 +1470,11 @@ internal class Av1TileReader : IAv1TileReader
}
}
+ ///
+ /// Reads and accumulates the loop-filter delta values carried by a coding block.
+ ///
+ /// The tile symbol decoder.
+ /// The current coding block and superblock delta storage.
private void ReadDeltaLoopFilter(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128Superblock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64;
@@ -1237,6 +1502,12 @@ internal class Av1TileReader : IAv1TileReader
}
}
+ ///
+ /// Reads or infers the residual-skip flag for a coding block.
+ ///
+ /// The tile symbol decoder.
+ /// The current coding block and its available neighbors.
+ /// when the block omits residual coefficients; otherwise, .
private bool ReadSkip(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
int segmentId = partitionInfo.ModeInfo.SegmentId;
@@ -1254,8 +1525,11 @@ internal class Av1TileReader : IAv1TileReader
}
///
- /// SVT: read_delta_qindex
+ /// Reads and accumulates a superblock quantizer-index delta when the block carries one.
///
+ /// The tile symbol decoder.
+ /// The current coding block and superblock quantizer storage.
+ /// Corresponds to read_delta_qindex in SVT-AV1.
private void ReadDeltaQuantizerIndex(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo)
{
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128Superblock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64;
@@ -1274,28 +1548,30 @@ internal class Av1TileReader : IAv1TileReader
}
}
+ ///
+ /// Determines whether a frame-relative mode-information position lies inside the active tile.
+ ///
+ /// The frame-relative mode-information row.
+ /// The frame-relative mode-information column.
+ /// when the position lies within the active tile; otherwise, .
private bool IsInside(int rowIndex, int columnIndex) =>
columnIndex >= this.FrameHeader.TilesInfo.TileColumnCount &&
columnIndex < this.FrameHeader.TilesInfo.TileColumnCount &&
rowIndex >= this.FrameHeader.TilesInfo.TileRowCount &&
rowIndex < this.FrameHeader.TilesInfo.TileRowCount;
- /*
- private static bool IsChroma(int rowIndex, int columnIndex, Av1BlockModeInfo blockMode, bool subSamplingX, bool subSamplingY)
- {
- int block4x4Width = blockMode.BlockSize.Get4x4WideCount();
- int block4x4Height = blockMode.BlockSize.Get4x4HighCount();
- bool xPos = (columnIndex & 0x1) > 0 || (block4x4Width & 0x1) > 0 || !subSamplingX;
- bool yPos = (rowIndex & 0x1) > 0 || (block4x4Height & 0x1) > 0 || !subSamplingY;
- return xPos && yPos;
- }*/
-
///
- /// SVT: partition_plane_context
+ /// Derives the partition entropy context from the current split bit of the above and left neighbors.
///
+ /// The partition origin in 4x4 mode-information units.
+ /// The square parent block size.
+ /// The active tile boundaries.
+ /// The containing superblock.
+ /// The partition entropy context.
+ /// Corresponds to partition_plane_context in SVT-AV1.
private int GetPartitionPlaneContext(Point location, Av1BlockSize blockSize, Av1TileInfo tileInfo, Av1SuperblockInfo superblockInfo)
{
- // Maximum partition point is 8x8. Offset the log value occordingly.
+ // The five stored split bits begin at the 8x8 partition point, so normalize the block-size log to that bit index.
int aboveCtx = this.aboveNeighborContext.AbovePartitionWidth[location.X - tileInfo.ModeInfoColumnStart];
int leftCtx = this.leftNeighborContext.LeftPartitionHeight[(location.Y - superblockInfo.ModeInfoPosition.Y) & Av1PartitionContext.Mask];
int blockSizeLog = blockSize.Get4x4WidthLog2() - Av1BlockSize.Block8x8.Get4x4WidthLog2();
@@ -1306,6 +1582,15 @@ internal class Av1TileReader : IAv1TileReader
return ((left << 1) + above) + (blockSizeLog * Av1Constants.PartitionProbabilitySet);
}
+ ///
+ /// Publishes the decoded partition sizes to the above and left neighbor contexts.
+ ///
+ /// The parent block origin in 4x4 mode-information units.
+ /// The active tile boundaries.
+ /// The containing superblock.
+ /// The primary size produced by the partition.
+ /// The parent block size.
+ /// The decoded partition type.
private void UpdatePartitionContext(Point modeInfoLocation, Av1TileInfo tileLoc, Av1SuperblockInfo superblockInfo, Av1BlockSize subSize, Av1BlockSize blockSize, Av1PartitionType partition)
{
if (blockSize >= Av1BlockSize.Block8x8)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
index 2ee25e9bd..eb67fabdd 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
@@ -10,11 +10,18 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Writes the partition, mode, transform, and coefficient syntax for one AV1 tile.
+///
internal partial class Av1TileWriter
{
- // Generates 5 bit field in which each bit set to 1 represents
- // a BlockSize partition 11111 means we split 128x128, 64x64, 32x32, 16x16
- // and 8x8. 10000 means we just split the 128x128 to 64x64
+ ///
+ /// Maps each AV1 block size to the five-bit partition contexts written to its bottom and right edges.
+ ///
+ ///
+ /// Each set bit represents a split level from 128x128 through 8x8. For example, 11111
+ /// records every split level, while 10000 records only the 128x128 split.
+ ///
private static readonly Av1PartitionContext[] PartitionContextLookup =
[
new(31, 31), // 4X4 - {0b11111, 0b11111}
@@ -41,11 +48,21 @@ internal partial class Av1TileWriter
new(16, 28), // 64X16 - {0b10000, 0b11100}
];
+ ///
+ /// Maps neighboring intra prediction modes to the key-frame luma-mode entropy contexts.
+ ///
private static readonly byte[] IntraModeContextLookup = [0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0];
///
- /// SVT: svt_aom_write_sb
+ /// Writes the partition tree and each final coding block for a superblock.
///
+ /// The picture coding state.
+ /// The entropy-coding position state for the superblock.
+ /// The tile symbol encoder.
+ /// The encoder decisions for the superblock.
+ /// The transformed coefficients for the frame.
+ /// The zero-based tile index.
+ /// Corresponds to svt_aom_write_sb in SVT-AV1.
public static void WriteSuperblock(
Av1PictureControlSet pcs,
Av1EntropyCodingContext ec_ctx,
@@ -57,7 +74,8 @@ internal partial class Av1TileWriter
Av1SequenceControlSet scs = pcs.Sequence;
Av1NeighborArrayUnit partitionContextNeighbors = pcs.PartitionContexts[tileIndex];
- // CU Varaiables
+ // The geometry scan includes both partition nodes and final coding blocks. These two indices
+ // advance independently because a split node consumes geometry without consuming FinalBlocks.
int blockIndex = 0;
uint finalBlockIndex = 0;
@@ -67,7 +85,7 @@ internal partial class Av1TileWriter
bool check_blk_out_of_bound = !sb_geom.IsComplete;
do
{
- bool code_blk_cond = true; // Code cu only if it is inside the picture
+ bool code_blk_cond = true;
Av1EncoderBlockStruct blk_ptr = superblock.FinalBlocks[finalBlockIndex];
Av1BlockGeometry blk_geom = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blockIndex);
@@ -75,9 +93,10 @@ internal partial class Av1TileWriter
Point blockOrigin = blk_geom.Origin;
Guard.IsTrue(bsize < Av1BlockSize.AllSizes, nameof(bsize), "Block size must be a valid value.");
- // assert(blk_geom->shape == PART_N);
if (check_blk_out_of_bound)
{
+ // Edge superblocks retain their complete geometry tree, but only nodes whose center or
+ // origin reaches the visible frame can contribute coding syntax.
code_blk_cond = (((blockOrigin.X + (blk_geom.BlockWidth / 2)) < pcs.Parent.AlignedWidth) ||
((blockOrigin.Y + (blk_geom.BlockHeight / 2)) < pcs.Parent.AlignedHeight)) &&
(blockOrigin.X < pcs.Parent.AlignedWidth && blockOrigin.Y < pcs.Parent.AlignedHeight);
@@ -126,7 +145,7 @@ internal partial class Av1TileWriter
}*/
}
- // Code Split Flag
+ // Blocks below 8x8 cannot be partition points in the AV1 syntax.
EncodePartition(
pcs,
ref writer,
@@ -136,7 +155,6 @@ internal partial class Av1TileWriter
partitionContextNeighbors);
}
- // assert(blk_geom.Shape == PART_N);
Guard.IsTrue(Av1Math.Implies(bsize == Av1BlockSize.Block4x4, superblock.CodingUnitPartitionTypes[blockIndex] == Av1PartitionType.None), nameof(bsize), string.Empty);
switch (superblock.CodingUnitPartitionTypes[blockIndex])
{
@@ -281,8 +299,15 @@ internal partial class Av1TileWriter
}
///
- /// SVT: encode_partition_av1
+ /// Writes a partition symbol using the above and left partition contexts available at a block origin.
///
+ /// The picture coding state.
+ /// The tile symbol encoder.
+ /// The square parent block size.
+ /// The selected partition type.
+ /// The block origin in samples.
+ /// The partition neighbor arrays for the tile.
+ /// Corresponds to encode_partition_av1 in SVT-AV1.
private static void EncodePartition(
Av1PictureControlSet pcs,
ref Av1SymbolEncoder writer,
@@ -322,6 +347,7 @@ internal partial class Av1TileWriter
Guard.IsTrue(blockSize.Get4x4WidthLog2() == blockSize.Get4x4HeightLog2(), nameof(blockSize), "Blocks need to be square.");
Guard.IsTrue(blockSizeLog2 >= 0, nameof(blockSizeLog2), "bsl needs to be a positive integer.");
+ // Each square block-size level owns four contexts selected by the current split bit of its neighbors.
context_index = ((left * 2) + above) + (blockSizeLog2 * Av1Constants.PartitionProbabilitySet);
if (!has_rows && !has_cols)
@@ -347,8 +373,16 @@ internal partial class Av1TileWriter
}
///
- /// SVT: write_modes_b
+ /// Writes the segmentation, prediction, transform, coefficient, and filter syntax for one final coding block.
///
+ /// The picture coding state.
+ /// The entropy-coding position state for the superblock.
+ /// The tile symbol encoder.
+ /// The containing superblock.
+ /// The final encoder decisions for the block.
+ /// The zero-based tile index.
+ /// The transformed coefficients for the frame.
+ /// Corresponds to write_modes_b in SVT-AV1.
private static void WriteModesBlock(
Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext,
@@ -402,8 +436,6 @@ internal partial class Av1TileWriter
blk_ptr.MacroBlock.LeftMacroBlock = null;
}
- // Not required, part of Av1SymbolEncoder.
- // blk_ptr.MacroBlock.tile_ctx = frame_context;
SetModeInfoRowAndColumn(
pcs,
blk_ptr.MacroBlock,
@@ -414,10 +446,8 @@ internal partial class Av1TileWriter
pcs.Parent.Common.ModeInfoRowCount,
pcs.Parent.Common.ModeInfoColumnCount);
- // if (pcs.slice_type == I_SLICE)
- // We implement only INTRA frames.
+ // This encoder path currently writes intra frames only, so every block follows the key-frame mode syntax.
{
- // const int32_t skip = write_skip(cm, xd, mbmi->segment_id, mi, w)
if (pcs.Parent.FrameHeader.SegmentationParameters.Enabled && pcs.Parent.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip)
{
WriteSegmentId(pcs, ref writer, blockGeometry.BlockSize, blockOrigin, blk_ptr, skipWritingCoefficients);
@@ -548,7 +578,6 @@ internal partial class Av1TileWriter
if (!skipWritingCoefficients)
{
- // SVT: av1_encode_coeff_1d
EncodeCoefficients1d(
pcs,
entropyCodingContext,
@@ -565,7 +594,7 @@ internal partial class Av1TileWriter
}
}
- // Update the neighbors
+ // Neighbor state must be updated after all symbols for the block have used the preceding contexts.
UpdateNeighbors(pcs, entropyCodingContext, blockOrigin, blk_ptr, tile_idx, blockSize);
if (IsPaletteAllowed(pcs.Parent.PaletteLevel, blockGeometry.BlockSize))
@@ -579,6 +608,16 @@ internal partial class Av1TileWriter
}
}
+ ///
+ /// Writes the chroma intra mode, chroma-from-luma alpha values, and directional angle adjustment for a block.
+ ///
+ /// The tile symbol encoder.
+ /// The selected block modes.
+ /// The encoder prediction-unit state.
+ /// The luma block size.
+ /// The selected luma prediction mode.
+ /// The selected chroma prediction mode.
+ /// A value indicating whether chroma-from-luma mode is available.
private static void EncodeIntraChromaMode(
ref Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo,
@@ -604,26 +643,24 @@ internal partial class Av1TileWriter
}
///
- /// Get the contexts (left and top) for writing the intra luma mode for key frames.
- /// Intended to be used for key frame only.
+ /// Gets the above and left key-frame contexts used to write an intra luma mode.
///
- /// SVT: svt_aom_get_kf_y_mode_ctx
+ /// The current macroblock and its mapped neighbors.
+ /// The context derived from the above luma mode.
+ /// The context derived from the left luma mode.
+ /// Corresponds to svt_aom_get_kf_y_mode_ctx in SVT-AV1.
private static void GetYModeContext(Av1MacroBlockD xd, out byte above_ctx, out byte left_ctx)
{
Av1PredictionMode intraLumaLeftMode = Av1PredictionMode.DC;
Av1PredictionMode intraLumaTopMode = Av1PredictionMode.DC;
if (xd.IsLeftAvailable)
{
- // When called for key frame, neighbouring mode should be intra
- // assert(!is_inter_block(&xd->mi[-1]->mbmi.block_mi) || is_intrabc_block(&xd->mi[-1]->mbmi.block_mi));
+ // Key-frame neighbors are intra blocks, so their luma modes directly select the context class.
intraLumaLeftMode = xd.ModeInfo[-1].MacroBlockModeInfo.Block.Mode;
}
if (xd.IsUpAvailable)
{
- // When called for key frame, neighbouring mode should be intra
- // assert(!is_inter_block(&xd->mi[-xd->mi_stride]->mbmi.block_mi) ||
- // is_intrabc_block(&xd->mi[-xd->mi_stride]->mbmi.block_mi));
intraLumaTopMode = xd.ModeInfo[-xd.ModeInfoStride].MacroBlockModeInfo.Block.Mode;
}
@@ -632,8 +669,14 @@ internal partial class Av1TileWriter
}
///
- /// SVT: encode_intra_luma_mode_kf_av1
+ /// Writes the key-frame luma prediction mode and any directional angle adjustment.
///
+ /// The tile symbol encoder.
+ /// The selected block modes.
+ /// The encoder prediction-unit state.
+ /// The block size.
+ /// The selected luma prediction mode.
+ /// Corresponds to encode_intra_luma_mode_kf_av1 in SVT-AV1.
private static void EncodeIntraLumaMode(
ref Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo,
@@ -650,6 +693,16 @@ internal partial class Av1TileWriter
}
}
+ ///
+ /// Writes luma and chroma palette-mode syntax for a block.
+ ///
+ /// The sequence coding state.
+ /// The tile symbol encoder.
+ /// The selected block modes.
+ /// The encoder block state.
+ /// The block size.
+ /// The block position in mode-information units.
+ /// Palette-mode encoding is not implemented.
private static void WritePaletteModeInfo(
Av1SequenceControlSet scs,
ref Av1SymbolEncoder writer,
@@ -687,8 +740,14 @@ internal partial class Av1TileWriter
}
///
- /// SVT: svt_aom_filter_intra_allowed
+ /// Determines whether filter-intra syntax is available for a block mode.
///
+ /// A value indicating whether the sequence enables filter-intra prediction.
+ /// The block size.
+ /// The selected luma palette size.
+ /// The selected luma prediction mode.
+ /// when the block can use filter-intra prediction; otherwise, .
+ /// Corresponds to svt_aom_filter_intra_allowed in SVT-AV1.
private static bool IsFilterIntraAllowed(
bool enableFilterIntra,
Av1BlockSize blockSize,
@@ -697,8 +756,12 @@ internal partial class Av1TileWriter
=> mode == Av1PredictionMode.DC && paletteSize == 0 && IsFilterIntraAllowedBlockSize(enableFilterIntra, blockSize);
///
- /// SVT: svt_aom_filter_intra_allowed_bsize
+ /// Determines whether filter-intra prediction is enabled for a block size.
///
+ /// A value indicating whether the sequence enables filter-intra prediction.
+ /// The block size.
+ /// when filter-intra prediction supports the block dimensions; otherwise, .
+ /// Corresponds to svt_aom_filter_intra_allowed_bsize in SVT-AV1.
private static bool IsFilterIntraAllowedBlockSize(bool enableFilterIntra, Av1BlockSize blockSize)
{
if (!enableFilterIntra)
@@ -710,8 +773,13 @@ internal partial class Av1TileWriter
}
///
- /// SVT: write_intrabc_info
+ /// Writes the intra-block-copy selection and displacement-vector syntax for a block.
///
+ /// The tile symbol encoder.
+ /// The selected block modes.
+ /// The encoder block state.
+ /// The displacement-vector syntax is not implemented when intra block copy is selected.
+ /// Corresponds to write_intrabc_info in SVT-AV1.
private static void WriteIntraBlockCopyInfo(
ref Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo,
@@ -734,14 +802,24 @@ internal partial class Av1TileWriter
}
///
- /// SVT: svt_aom_allow_intrabc
+ /// Determines whether the current frame permits intra block copy.
///
+ /// The current frame header.
+ /// when both screen-content tools and intra block copy are enabled; otherwise, .
+ /// Corresponds to svt_aom_allow_intrabc in SVT-AV1.
private static bool IsIntraBlockCopyAllowed(ObuFrameHeader frameHeader)
=> frameHeader.AllowScreenContentTools && frameHeader.AllowIntraBlockCopy;
///
- /// SVT: ec_update_neighbors
+ /// Updates partition and coefficient neighbor arrays after writing a block.
///
+ /// The picture coding state.
+ /// The entropy-coding position state for the superblock.
+ /// The block origin in samples.
+ /// The encoder block state.
+ /// The zero-based tile index.
+ /// The block size.
+ /// Corresponds to ec_update_neighbors in SVT-AV1.
private static void UpdateNeighbors(
Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext,
@@ -758,7 +836,7 @@ internal partial class Av1TileWriter
Av1MacroBlockModeInfo mbmi = pcs.GetMacroBlockModeInfo(blockOrigin);
bool skip_coeff = mbmi.Block.Skip;
- // Update the Leaf Depth Neighbor Array
+ // Store the block-size split mask across the edges that future partition symbols can observe.
Av1PartitionContext partition = new(
PartitionContextLookup[(int)blockSize].Above,
PartitionContextLookup[(int)blockSize].Left);
@@ -771,6 +849,8 @@ internal partial class Av1TileWriter
Av1NeighborArrayUnit.UnitMask.Left | Av1NeighborArrayUnit.UnitMask.Top);
if (skip_coeff)
{
+ // A skipped block has an all-zero residual, so publish a zero sign/level context over its edges
+ // and advance coefficient positions without reading transform units.
byte dcSignLevelCoefficient = 0;
Span dcSignSpan = new(ref dcSignLevelCoefficient);
@@ -800,8 +880,12 @@ internal partial class Av1TileWriter
}
///
- /// SVT: svt_av1_allow_palette
+ /// Determines whether the encoder palette level and block dimensions permit palette mode.
///
+ /// The nonzero encoder palette level.
+ /// The block size.
+ /// when palette mode is enabled for the block; otherwise, .
+ /// Corresponds to svt_av1_allow_palette in SVT-AV1.
private static bool IsPaletteAllowed(int allowPalette, Av1BlockSize blockSize)
{
Guard.MustBeLessThan((int)blockSize, (int)Av1BlockSize.AllSizes, nameof(blockSize));
@@ -812,8 +896,12 @@ internal partial class Av1TileWriter
}
///
- /// SVT: svt_aom_allow_palette
+ /// Determines whether screen-content tools and block dimensions permit palette mode.
///
+ /// A value indicating whether screen-content tools are enabled.
+ /// The block size.
+ /// when palette mode is available for the block; otherwise, .
+ /// Corresponds to svt_aom_allow_palette in SVT-AV1.
private static bool IsPaletteAllowed(bool allowScreenContentTools, Av1BlockSize blockSize)
=> allowScreenContentTools &&
blockSize.GetWidth() <= 64 &&
@@ -821,8 +909,15 @@ internal partial class Av1TileWriter
blockSize >= Av1BlockSize.Block8x8;
///
- /// SVT: write_cdef
+ /// Writes the constrained directional enhancement filter strength at its first coded block in a filter unit.
///
+ /// The sequence coding state.
+ /// The picture coding state.
+ /// The tile symbol encoder.
+ /// The zero-based tile index.
+ /// A value indicating whether the current block omits residual coefficients.
+ /// The block position in 4x4 mode-information units.
+ /// Corresponds to write_cdef in SVT-AV1.
private static void WriteCdef(
Av1SequenceControlSet scs,
Av1PictureControlSet pcs,
@@ -836,7 +931,7 @@ internal partial class Av1TileWriter
if (frameHeader.CodedLossless || frameHeader.AllowIntraBlockCopy)
{
- // Initialize to indicate no CDEF for safety.
+ // Lossless and intra-block-copy frames disable CDEF, so normalize the header to its single zero-strength form.
frameHeader.CdefParameters.BitCount = 0;
frameHeader.CdefParameters.YStrength[0] = 0;
frameHeader.CdefParameters.UvStrength[0] = 0;
@@ -849,18 +944,17 @@ internal partial class Av1TileWriter
// cm->mi_grid_visible[(mi_row & m) * cm->mi_stride + (mi_col & m)];
Av1ModeInfo mi = pcs.GetFromModeInfoGrid(modeInfoPosition)[0];
- // Initialise when at top left part of the superblock
+ // Each superblock begins with all contained 64x64 filter units unassigned.
if ((modeInfoPosition.Y & (scs.SequenceHeader.SuperblockModeInfoSize - 1)) == 0 &&
(modeInfoPosition.X & (scs.SequenceHeader.SuperblockModeInfoSize - 1)) == 0)
{
- // Top left?
pcs.CdefPreset[tileIndex][0] = -1;
pcs.CdefPreset[tileIndex][1] = -1;
pcs.CdefPreset[tileIndex][2] = -1;
pcs.CdefPreset[tileIndex][3] = -1;
}
- // Emit CDEF param at first non-skip coding block
+ // The strength is coded once, at the first non-skipped block in each 64x64 CDEF filter unit.
int mask = 1 << (6 - Av1Constants.ModeInfoSizeLog2);
int index = scs.SequenceHeader.Use128x128Superblock ? Math.Max(1, modeInfoPosition.X & mask) + (2 * Math.Max(1, modeInfoPosition.Y & mask)) : 0;
@@ -872,8 +966,17 @@ internal partial class Av1TileWriter
}
///
- /// SVT: set_mi_row_col
+ /// Populates a macroblock's frame edges, tile-neighbor availability, and rectangular-partition context.
///
+ /// The picture coding state.
+ /// The macroblock state to populate.
+ /// The active tile boundaries.
+ /// The block position in 4x4 mode-information units.
+ /// The block size.
+ /// The row stride of the mode-information grid.
+ /// The coded frame height in mode-information rows.
+ /// The coded frame width in mode-information columns.
+ /// Corresponds to set_mi_row_col in SVT-AV1.
private static void SetModeInfoRowAndColumn(
Av1PictureControlSet pcs,
Av1MacroBlockD macroBlock,
@@ -891,7 +994,7 @@ internal partial class Av1TileWriter
macroBlock.ModeInfoStride = modeInfoStride;
- // Are edges available for intra prediction?
+ // Prediction cannot cross tile boundaries even when frame mode information exists there.
macroBlock.IsUpAvailable = modeInfoPosition.Y > tile.ModeInfoRowStart;
macroBlock.IsLeftAvailable = modeInfoPosition.X > tile.ModeInfoColumnStart;
macroBlock.ModeInfo = pcs.GetFromModeInfoGrid(modeInfoPosition);
@@ -918,9 +1021,8 @@ internal partial class Av1TileWriter
macroBlock.IsSecondRectangle = false;
if (macroBlock.N8Size.Width < macroBlock.N8Size.Height)
{
- // Only mark is_sec_rect as 1 for the last block.
- // For PARTITION_VERT_4, it would be (0, 0, 0, 1);
- // For other partitions, it would be (0, 1).
+ // Only the last sub-block of a rectangular partition selects the secondary transform context.
+ // Vertical-four therefore maps to (0, 0, 0, 1), while two-way partitions map to (0, 1).
if (((modeInfoPosition.X + macroBlock.N8Size.Width) & (macroBlock.N8Size.Height - 1)) == 0)
{
macroBlock.IsSecondRectangle = true;
@@ -937,8 +1039,22 @@ internal partial class Av1TileWriter
}
///
- /// SVT: av1_encode_coeff_1d
+ /// Writes luma and chroma transform coefficients for a block in plane order.
///
+ /// The picture coding state.
+ /// The entropy-coding position state for the superblock.
+ /// The tile symbol encoder.
+ /// The selected macroblock modes.
+ /// The encoder block state.
+ /// The block origin in samples.
+ /// The luma prediction direction.
+ /// The luma block size.
+ /// The transformed coefficients for the frame.
+ /// The luma coefficient neighbor contexts.
+ /// The red-difference chroma coefficient neighbor contexts.
+ /// The blue-difference chroma coefficient neighbor contexts.
+ /// The transform-depth path required by the block is not implemented.
+ /// Corresponds to av1_encode_coeff_1d in SVT-AV1.
private static void EncodeCoefficients1d(
Av1PictureControlSet pcs,
Av1EntropyCodingContext ec_ctx,
@@ -987,8 +1103,19 @@ internal partial class Av1TileWriter
}
///
- /// SVT: av1_encode_tx_coef_y
+ /// Writes each luma transform block and updates its DC-sign and coefficient-level neighbor contexts.
///
+ /// The picture coding state.
+ /// The entropy-coding position state for the superblock.
+ /// The tile symbol encoder.
+ /// The selected macroblock modes.
+ /// The encoder block state.
+ /// The block origin in samples.
+ /// The luma prediction direction.
+ /// The luma block size.
+ /// The transformed coefficients for the frame.
+ /// The luma coefficient neighbor contexts.
+ /// Corresponds to av1_encode_tx_coef_y in SVT-AV1.
public static void EncodeTransformCoefficientsY(
Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext,
@@ -1001,7 +1128,7 @@ internal partial class Av1TileWriter
Av1FrameBuffer coeff_ptr,
Av1NeighborArrayUnit luma_dc_sign_level_coeff_na)
{
- // Removed any code related to INTER frames.
+ // This writer currently emits intra frames, so coefficient contexts use only intra prediction state.
Av1BlockGeometry blockGeometry = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blk_ptr.ModeDecisionScanIndex);
int tx_depth = mbmi.Block.TransformDepth;
int txb_count = blockGeometry.TransformBlockCount[mbmi.Block.TransformDepth];
@@ -1031,7 +1158,7 @@ internal partial class Av1TileWriter
int eob = blk_ptr.TransformBlocks[txb_itr].NzCoefficientCount[0];
if (eob == 0)
{
- // INTRA
+ // AV1 requires the canonical DCT_DCT transform type when a transform block has no coefficients.
tx_type = blk_ptr.TransformBlocks[txb_itr].TransformType[(int)Av1PlaneType.Y] = Av1TransformType.DctDct;
Guard.IsTrue(tx_type == Av1TransformType.DctDct, nameof(tx_type), string.Empty);
}
@@ -1047,7 +1174,8 @@ internal partial class Av1TileWriter
frameHeader.UseReducedTransformSet,
blk_ptr.FilterIntraMode);
- // Update the luma Dc Sign Level Coeff Neighbor Array
+ // WriteCoefficients packs the DC sign and cumulative level into one integer; publish its bytes
+ // across the transform edges so the next blocks derive identical entropy contexts.
Span culLevelSpan = new(ref cul_level_y);
ReadOnlySpan dc_sign_level_coeff = MemoryMarshal.AsBytes(culLevelSpan);
@@ -1064,8 +1192,20 @@ internal partial class Av1TileWriter
}
///
- /// SVT: av1_encode_tx_coef_uv
+ /// Writes both chroma transform blocks and updates their DC-sign and coefficient-level neighbor contexts.
///
+ /// The picture coding state.
+ /// The entropy-coding position state for the superblock.
+ /// The tile symbol encoder.
+ /// The selected macroblock modes.
+ /// The encoder block state.
+ /// The luma block origin in samples.
+ /// The luma prediction direction used by coefficient contexts.
+ /// The luma block size.
+ /// The transformed coefficients for the frame.
+ /// The red-difference chroma coefficient neighbor contexts.
+ /// The blue-difference chroma coefficient neighbor contexts.
+ /// Corresponds to av1_encode_tx_coef_uv in SVT-AV1.
private static void EncodeTransformCoefficientsUv(
Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext,
@@ -1098,7 +1238,7 @@ internal partial class Av1TileWriter
if (blockGeometry.HasUv)
{
- // cb
+ // Both chroma planes share transform geometry but retain independent coefficient contexts.
Span coeff_buffer = coeff_ptr.BufferCb!.DangerousGetSingleSpan().Slice(entropyCodingContext.CodedAreaSuperblockUv);
Av1TransformBlockContext blockContext = new();
Point transformOrigin = blockGeometry.TransformOrigin[tx_depth][tx_index];
@@ -1123,7 +1263,6 @@ internal partial class Av1TileWriter
frameHeader.UseReducedTransformSet,
blk_ptr.FilterIntraMode);
- // cr
coeff_buffer = coeff_ptr.BufferCr!.DangerousGetSingleSpan().Slice(entropyCodingContext.CodedAreaSuperblockUv);
blockContext = new();
int endOfBlockCr = blk_ptr.TransformBlocks[tx_index].NzCoefficientCount[2];
@@ -1148,7 +1287,7 @@ internal partial class Av1TileWriter
frameHeader.UseReducedTransformSet,
blk_ptr.FilterIntraMode);
- // Update the cb Dc Sign Level Coeff Neighbor Array
+ // Publish each plane's packed sign/level summary across its transform edges.
Span culLevelCbSpan = new(ref cul_level_cb);
ReadOnlySpan dc_sign_level_coeff = MemoryMarshal.AsBytes(culLevelCbSpan);
cb_dc_sign_level_coeff_na.UnitModeWrite(
@@ -1157,7 +1296,6 @@ internal partial class Av1TileWriter
new Size(transformWidth, transformHeight),
Av1NeighborArrayUnit.UnitMask.Top | Av1NeighborArrayUnit.UnitMask.Left);
- // Update the cr DC Sign Level Coeff Neighbor Array
Span culLevelCrSpan = new(ref cul_level_cr);
dc_sign_level_coeff = MemoryMarshal.AsBytes(culLevelCrSpan);
cr_dc_sign_level_coeff_na.UnitModeWrite(
@@ -1171,11 +1309,24 @@ internal partial class Av1TileWriter
}
}
+ ///
+ /// Rounds a luma sample position down to the 8-sample alignment used before chroma subsampling.
+ ///
+ /// The luma sample position.
+ /// The aligned luma position.
private static Point RoundUv(Point point) => (point >> 3) << 3;
///
- /// SVT: svt_aom_get_txb_ctx
+ /// Derives coefficient skip and DC-sign contexts from the transform block's above and left neighbors.
///
+ /// The picture coding state.
+ /// The luma or chroma component class.
+ /// The packed DC-sign and coefficient-level neighbor contexts.
+ /// The transform-block origin in samples of the target plane.
+ /// The containing block size on the target plane.
+ /// The transform size.
+ /// The context object to populate.
+ /// Corresponds to svt_aom_get_txb_ctx in SVT-AV1.
private static void GetTransformBlockContexts(
Av1PictureControlSet pcs,
Av1ComponentType plane,
@@ -1207,6 +1358,8 @@ internal partial class Av1TileWriter
byte sign;
+ // The high bits encode the DC sign class: zero, negative, or positive. Summing classes over
+ // both edges selects whether neighboring DC coefficients bias the current sign context.
if (dcSignLevelCoefficientNeighborArray.Top[dcSignLevelCoefficientTopNeighborIndex] != Av1NeighborArrayUnit.InvalidNeighborData)
{
do
@@ -1253,6 +1406,7 @@ internal partial class Av1TileWriter
}
else
{
+ // Luma skip contexts depend on the minimum and union of the clipped edge levels.
byte[][] skip_contexts = [
[1, 2, 2, 2, 3], [1, 4, 4, 4, 5], [1, 4, 4, 4, 5], [1, 4, 4, 4, 5], [1, 4, 4, 4, 6]
];
@@ -1292,6 +1446,8 @@ internal partial class Av1TileWriter
}
else
{
+ // Chroma contexts use only the presence of nonzero levels on each edge, plus an offset
+ // that distinguishes a transform smaller than its containing plane block.
short ctx_base_left = 0;
short ctx_base_top = 0;
@@ -1324,6 +1480,15 @@ internal partial class Av1TileWriter
}
}
+ ///
+ /// Writes or predicts a block segment identifier and updates the frame segmentation map.
+ ///
+ /// The picture coding state.
+ /// The tile symbol encoder.
+ /// The block size.
+ /// The block origin in samples.
+ /// The encoder block state.
+ /// A value indicating whether residual coefficients are omitted.
private static void WriteSegmentId(Av1PictureControlSet pcs, ref Av1SymbolEncoder writer, Av1BlockSize blockSize, Point blockOrigin, Av1EncoderBlockStruct block, bool skip)
{
ObuSegmentationParameters segmentation_params = pcs.Parent.FrameHeader.SegmentationParameters;
@@ -1335,6 +1500,7 @@ internal partial class Av1TileWriter
int spatial_pred = GetSpatialSegmentationPrediction(pcs, block.MacroBlock, blockOrigin, out int cdf_num);
if (skip)
{
+ // With segment-id-before-skip syntax, a skipped block inherits the spatial predictor without coding a residual ID.
pcs.UpdateSegmentation(blockSize, blockOrigin, spatial_pred);
block.SegmentId = spatial_pred;
return;
@@ -1346,17 +1512,24 @@ internal partial class Av1TileWriter
}
///
- /// SVT: svt_av1_get_spatial_seg_prediction
+ /// Derives a segment identifier predictor and entropy context from the upper-left, above, and left neighbors.
///
+ /// The picture coding state.
+ /// The current macroblock and its neighbor availability.
+ /// The block origin in samples.
+ /// The entropy context selected by matching neighbor identifiers.
+ /// The spatially predicted segment identifier.
+ /// Corresponds to svt_av1_get_spatial_seg_prediction in SVT-AV1.
private static int GetSpatialSegmentationPrediction(
Av1PictureControlSet pcs,
Av1MacroBlockD xd,
Point blockOrigin,
out int cdf_index)
{
- int prev_ul = -1; // top left segment_id
- int prev_l = -1; // left segment_id
- int prev_u = -1; // top segment_id
+ const int unavailableSegmentId = -1;
+ int prev_ul = unavailableSegmentId;
+ int prev_l = unavailableSegmentId;
+ int prev_u = unavailableSegmentId;
int mi_col = blockOrigin.X >> Av1Constants.ModeInfoSizeLog2;
int mi_row = blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2;
@@ -1380,8 +1553,8 @@ internal partial class Av1TileWriter
prev_l = Av1SymbolContextHelper.GetSegmentId(cm, segmentation_map, Av1BlockSize.Block4x4, new Point(mi_row - 0, mi_col - 1));
}
- // Pick CDF index based on number of matching/out-of-bounds segment IDs.
- // Edge case
+ // The entropy context records whether zero, two, or all three neighboring IDs agree.
+ // Any unavailable neighbor falls back to the least-specific context.
if (prev_ul < 0 || prev_u < 0 || prev_l < 0)
{
cdf_index = 0;
@@ -1399,15 +1572,13 @@ internal partial class Av1TileWriter
cdf_index = 0;
}
- // If 2 or more are identical returns that as predictor, otherwise prev_l.
- // edge case
- if (prev_u == -1)
+ // Select the majority value when possible; otherwise AV1 gives the left neighbor precedence.
+ if (prev_u == unavailableSegmentId)
{
- return prev_l == -1 ? 0 : prev_l;
+ return prev_l == unavailableSegmentId ? 0 : prev_l;
}
- // edge case
- if (prev_l == -1)
+ if (prev_l == unavailableSegmentId)
{
return prev_u;
}
@@ -1415,6 +1586,12 @@ internal partial class Av1TileWriter
return (prev_ul == prev_u) ? prev_u : prev_l;
}
+ ///
+ /// Writes the block skip flag using the sum of available above and left skip states as its context.
+ ///
+ /// The tile symbol encoder.
+ /// The encoder block state.
+ /// The skip value to write.
internal static void EncodeSkipCoefficients(ref Av1SymbolEncoder writer, Av1EncoderBlockStruct block, bool skip)
{
Av1MacroBlockModeInfo? above_mi = block.MacroBlock.AboveMacroBlock;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformBlockContext.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformBlockContext.cs
index 6256867e7..0f15b2231 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformBlockContext.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformBlockContext.cs
@@ -3,9 +3,18 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Carries the neighboring coefficient contexts used to entropy-code an AV1 transform block.
+///
internal class Av1TransformBlockContext
{
+ ///
+ /// Gets or sets the context used to decode the sign of the DC coefficient.
+ ///
public int DcSignContext { get; set; }
+ ///
+ /// Gets or sets the neighboring transform-block skip context.
+ ///
public int SkipContext { get; set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformInfo.cs
index dab777ed3..8cff3a790 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformInfo.cs
@@ -6,12 +6,12 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
///
-/// Information of a single Transform Block.
+/// Describes the size, position, type, and residual state of one AV1 transform block.
///
internal class Av1TransformInfo
{
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class with a 4x4 transform at the origin.
///
public Av1TransformInfo()
: this(Av1TransformSize.Size4x4, 0, 0)
@@ -21,6 +21,9 @@ internal class Av1TransformInfo
///
/// Initializes a new instance of the class.
///
+ /// The transform size.
+ /// The horizontal offset in mode-information units.
+ /// The vertical offset in mode-information units.
public Av1TransformInfo(Av1TransformSize size, int offsetX, int offsetY)
{
this.Size = size;
@@ -40,35 +43,35 @@ internal class Av1TransformInfo
}
///
- /// Gets or sets the transform size to be used for this Transform Block.
+ /// Gets or sets the transform size used for this transform block.
///
public Av1TransformSize Size { get; internal set; }
///
- /// Gets or sets the transform type to be used for this Transform Block.
+ /// Gets or sets the transform type used for this transform block.
///
public Av1TransformType Type { get; internal set; }
///
- /// Gets or sets the X offset of this block in ModeInfo units.
+ /// Gets or sets the horizontal offset of this block in mode-information units.
///
public int OffsetX { get; internal set; }
///
- /// Gets or sets the Y offset of this block in ModeInfo units.
+ /// Gets or sets the vertical offset of this block in mode-information units.
///
public int OffsetY { get; internal set; }
///
- /// Gets or sets a value indicating whether the Code block flag is set.
+ /// Gets or sets a value indicating whether the transform block contains a coded residual.
///
/// -
/// false
- /// No residual for the block
+ /// The block has no residual.
///
/// -
/// true
- /// Residual exists for the block
+ /// The block has a residual.
///
///
///
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformUnit.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformUnit.cs
index 6670f99ed..2755a37a9 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformUnit.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TransformUnit.cs
@@ -5,9 +5,18 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
+///
+/// Stores the transform syntax and coefficient range for one AV1 transform unit.
+///
internal class Av1TransformUnit
{
+ ///
+ /// Gets the nonzero-coefficient count for each color plane.
+ ///
public ushort[] NzCoefficientCount { get; } = new ushort[3];
+ ///
+ /// Gets the transform type selected for each color plane.
+ ///
public Av1TransformType[] TransformType { get; } = new Av1TransformType[Av1Constants.PlaneTypeCount];
}