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; }