diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaContext.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaContext.cs
index 9eb92971a..9cdc39889 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaContext.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaContext.cs
@@ -7,15 +7,40 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.ChromaFromLuma;
+///
+/// Accumulates subsampled luma samples and derives the zero-mean Q3 predictor surface used by AV1 chroma-from-luma prediction.
+///
internal class Av1ChromaFromLumaContext
{
+ ///
+ /// The fixed row stride and maximum dimension, in chroma samples, of the luma predictor buffer.
+ ///
private const int BufferLine = 32;
+ ///
+ /// The number of initialized predictor rows currently stored in .
+ ///
private int bufferHeight;
+
+ ///
+ /// The number of initialized predictor columns currently stored in .
+ ///
private int bufferWidth;
+
+ ///
+ /// Whether luma is subsampled by two along the horizontal axis for the chroma planes.
+ ///
private readonly bool subX;
+
+ ///
+ /// Whether luma is subsampled by two along the vertical axis for the chroma planes.
+ ///
private readonly bool subY;
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The AV1 color configuration that supplies chroma subsampling.
public Av1ChromaFromLumaContext(ObuColorConfig colorConfig)
{
this.subX = colorConfig.SubSamplingX;
@@ -23,10 +48,28 @@ internal class Av1ChromaFromLumaContext
this.Q3Buffer = new short[BufferLine * BufferLine];
}
+ ///
+ /// Gets the fixed-stride luma predictor samples in signed Q3 fixed-point representation.
+ ///
public short[] Q3Buffer { get; }
+ ///
+ /// Gets a value indicating whether edge padding and mean subtraction have been applied to the current samples.
+ ///
public bool AreParametersComputed { get; private set; }
+ ///
+ /// Stores one reconstructed luma transform region in the chroma-resolution Q3 predictor buffer.
+ ///
+ /// The integer sample type of the reconstructed luma plane.
+ /// The reconstructed luma samples for the transform region.
+ /// The distance, in samples, between consecutive input rows.
+ /// The transform row relative to the chroma-from-luma block, in mode-info units.
+ /// The transform column relative to the chroma-from-luma block, in mode-info units.
+ /// The luma transform dimensions.
+ /// The coded luma block size used to resolve shared sub-8x8 chroma ownership.
+ /// The frame-relative luma row in 4x4 mode-info units.
+ /// The frame-relative luma column in 4x4 mode-info units.
public void Store(
Span input,
int inputStride,
@@ -60,6 +103,8 @@ internal class Av1ChromaFromLumaContext
int storeColumn = column << (Av1Constants.ModeInfoSizeLog2 - subX);
int storeWidth = width >> subX;
int storeHeight = height >> subY;
+
+ // New luma samples invalidate the previously padded, zero-mean surface.
this.AreParametersComputed = false;
if (column == 0 && row == 0)
@@ -122,6 +167,10 @@ internal class Av1ChromaFromLumaContext
}
}
+ ///
+ /// Pads the populated predictor extent to the transform dimensions and subtracts its rounded mean.
+ ///
+ /// The chroma prediction transform dimensions.
public void ComputeParameters(Av1TransformSize transformSize)
{
Guard.IsFalse(this.AreParametersComputed, nameof(this.AreParametersComputed), "Do not call cfl_compute_parameters multiple time on the same values.");
@@ -130,6 +179,11 @@ internal class Av1ChromaFromLumaContext
this.AreParametersComputed = true;
}
+ ///
+ /// Extends the last initialized column and row to cover the requested predictor dimensions.
+ ///
+ /// The required predictor width in chroma samples.
+ /// The required predictor height in chroma samples.
private void Pad(int width, int height)
{
int differenceWidth = width - this.bufferWidth;
@@ -138,6 +192,8 @@ internal class Av1ChromaFromLumaContext
if (differenceWidth > 0)
{
int minimumHeight = height - differenceHeight;
+
+ // AV1 CfL edge extension repeats the final available sample when the coded luma extent is narrower.
for (int y = 0; y < minimumHeight; y++)
{
int rowOffset = y * BufferLine;
@@ -150,6 +206,7 @@ internal class Av1ChromaFromLumaContext
if (differenceHeight > 0)
{
+ // Missing bottom rows repeat the last available row after horizontal extension is complete.
for (int y = this.bufferHeight; y < height; y++)
{
int rowOffset = y * BufferLine;
@@ -160,14 +217,18 @@ internal class Av1ChromaFromLumaContext
}
}
- /************************************************************************************************
- * svt_subtract_average_c
- * Calculate the DC value by averaging over all sample. Subtract DC value to get AC values In C
- ************************************************************************************************/
+ ///
+ /// Subtracts the rounded Q3 average from each predictor sample, leaving the AC contribution used by CfL.
+ ///
+ /// The populated predictor dimensions.
+ /// SVT-AV1: svt_subtract_average_c.
private void SubtractAverage(Av1TransformSize transformSize)
{
int width = transformSize.GetWidth();
int height = transformSize.GetHeight();
+
+ // Transform dimensions are powers of two, so division by the sample count is an exact right shift.
+ // Half the sample count is accumulated first to round the signed Q3 mean to the nearest integer.
int roundOffset = (width * height) >> 1;
int pelCountLog2 = transformSize.GetBlockWidthLog2() + transformSize.GetBlockHeightLog2();
int sumQ3 = roundOffset;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaMath.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaMath.cs
index 8f3fce016..36106563f 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaMath.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/ChromaFromLuma/Av1ChromaFromLumaMath.cs
@@ -3,24 +3,75 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.ChromaFromLuma;
+///
+/// Provides AV1 chroma-from-luma sign, magnitude-index, and entropy-context mappings.
+///
internal static class Av1ChromaFromLumaMath
{
+ ///
+ /// The number of alpha sign states: zero, negative, and positive.
+ ///
private const int Signs = 3;
+
+ ///
+ /// The number of bits occupied by each plane's packed alpha-magnitude index.
+ ///
private const int AlphabetSizeLog2 = 4;
+ ///
+ /// The alpha sign value representing a zero multiplier.
+ ///
public const int SignZero = 0;
+
+ ///
+ /// The alpha sign value representing a negative multiplier.
+ ///
public const int SignNegative = 1;
+
+ ///
+ /// The alpha sign value representing a positive multiplier.
+ ///
public const int SignPositive = 2;
+ ///
+ /// Extracts the U-plane sign from a joint chroma sign symbol.
+ ///
+ /// The coded joint U/V sign symbol.
+ /// The U-plane sign state.
public static int SignU(int jointSign) => ((jointSign + 1) * 11) >> 5;
+ ///
+ /// Extracts the V-plane sign from a joint chroma sign symbol.
+ ///
+ /// The coded joint U/V sign symbol.
+ /// The V-plane sign state.
public static int SignV(int jointSign) => (jointSign + 1) - (Signs * SignU(jointSign));
+ ///
+ /// Extracts the U-plane alpha-magnitude index from the high four bits of the packed index.
+ ///
+ /// The packed U/V alpha-magnitude index.
+ /// The U-plane magnitude index.
public static int IndexU(int index) => index >> AlphabetSizeLog2;
- public static int IndexV(int index) => index & (AlphabetSizeLog2 - 1);
+ ///
+ /// Extracts the V-plane alpha-magnitude index from the low four bits of the packed index.
+ ///
+ /// The packed U/V alpha-magnitude index.
+ /// The V-plane magnitude index.
+ public static int IndexV(int index) => index & ((1 << AlphabetSizeLog2) - 1);
+ ///
+ /// Maps a joint sign symbol to the entropy context used for the U-plane alpha magnitude.
+ ///
+ /// The coded joint U/V sign symbol.
+ /// The U-plane alpha entropy context.
public static int ContextU(int jointSign) => jointSign + 1 - Signs;
+ ///
+ /// Maps a joint sign symbol to the symmetric entropy context used for the V-plane alpha magnitude.
+ ///
+ /// The coded joint U/V sign symbol.
+ /// The V-plane alpha entropy context.
public static int ContextV(int jointSign) => (SignV(jointSign) * Signs) + SignU(jointSign) - Signs;
}