diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcFillPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcFillPredictor.cs
index d4340c9c5..cf4a9dd89 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcFillPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcFillPredictor.cs
@@ -6,28 +6,56 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block with the sample-domain midpoint when neither top nor left neighbors are available.
+///
internal class Av1DcFillPredictor : IAv1Predictor
{
+ ///
+ /// The number of samples written to each destination row.
+ ///
private readonly uint blockWidth;
+
+ ///
+ /// The number of destination rows.
+ ///
private readonly uint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1DcFillPredictor(Size blockSize)
{
this.blockWidth = (uint)blockSize.Width;
this.blockHeight = (uint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1DcFillPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (uint)transformSize.GetWidth();
this.blockHeight = (uint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block with the 8-bit midpoint value.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The unused top-neighbor buffer required by the common predictor signature.
+ /// The unused left-neighbor buffer required by the common predictor signature.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1DcFillPredictor(transformSize).PredictScalar(destination, stride, above, left);
+ ///
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
+ // With no reference edge, AV1 uses the midpoint of the unsigned 8-bit sample domain as the DC predictor.
const byte expectedDc = 0x80;
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination));
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcLeftPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcLeftPredictor.cs
index 9237fe751..e3361f1a4 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcLeftPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcLeftPredictor.cs
@@ -6,26 +6,53 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block from the rounded average of its available left neighboring samples.
+///
internal class Av1DcLeftPredictor : IAv1Predictor
{
+ ///
+ /// The number of samples written to each destination row.
+ ///
private readonly uint blockWidth;
+
+ ///
+ /// The number of left samples averaged and destination rows written.
+ ///
private readonly uint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1DcLeftPredictor(Size blockSize)
{
this.blockWidth = (uint)blockSize.Width;
this.blockHeight = (uint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1DcLeftPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (uint)transformSize.GetWidth();
this.blockHeight = (uint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block from its left neighboring samples.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The unused top-neighbor buffer required by the common predictor signature.
+ /// The left neighboring samples.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1DcLeftPredictor(transformSize).PredictScalar(destination, stride, above, left);
+ ///
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
int sum = 0;
@@ -39,6 +66,7 @@ internal class Av1DcLeftPredictor : IAv1Predictor
sum += Unsafe.Add(ref leftRef, i);
}
+ // Adding half the sample count implements the normative nearest-integer DC average before division.
byte expectedDc = (byte)((sum + (this.blockHeight >> 1)) / this.blockHeight);
for (uint r = 0; r < this.blockHeight; r++)
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcPredictor.cs
index c7e9d2a34..6977ee40e 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcPredictor.cs
@@ -6,26 +6,53 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block from the rounded average of its top and left neighboring samples.
+///
internal class Av1DcPredictor : IAv1Predictor
{
+ ///
+ /// The number of top samples averaged and samples written to each destination row.
+ ///
private readonly nuint blockWidth;
+
+ ///
+ /// The number of left samples averaged and destination rows written.
+ ///
private readonly nuint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1DcPredictor(Size blockSize)
{
this.blockWidth = (nuint)blockSize.Width;
this.blockHeight = (nuint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1DcPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (nuint)transformSize.GetWidth();
this.blockHeight = (nuint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block from its top and left neighboring samples.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples.
+ /// The left neighboring samples.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1DcPredictor(transformSize).PredictScalar(destination, stride, above, left);
+ ///
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
int sum = 0;
@@ -48,6 +75,7 @@ internal class Av1DcPredictor : IAv1Predictor
sum += Unsafe.Add(ref leftRef, i);
}
+ // Adding half the combined edge count implements the normative nearest-integer DC average before division.
byte expectedDc = (byte)((sum + (count >> 1)) / count);
for (nuint r = 0; r < this.blockHeight; r++)
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcTopPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcTopPredictor.cs
index 93c98fcaa..4d4e85e14 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcTopPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcTopPredictor.cs
@@ -6,26 +6,53 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block from the rounded average of its available top neighboring samples.
+///
internal class Av1DcTopPredictor : IAv1Predictor
{
+ ///
+ /// The number of top samples averaged and samples written to each destination row.
+ ///
private readonly uint blockWidth;
+
+ ///
+ /// The number of destination rows.
+ ///
private readonly uint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1DcTopPredictor(Size blockSize)
{
this.blockWidth = (uint)blockSize.Width;
this.blockHeight = (uint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1DcTopPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (uint)transformSize.GetWidth();
this.blockHeight = (uint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block from its top neighboring samples.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples.
+ /// The unused left-neighbor buffer required by the common predictor signature.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1DcTopPredictor(transformSize).PredictScalar(destination, stride, above, left);
+ ///
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
int sum = 0;
@@ -39,6 +66,7 @@ internal class Av1DcTopPredictor : IAv1Predictor
sum += Unsafe.Add(ref aboveRef, i);
}
+ // Adding half the sample count implements the normative nearest-integer DC average before division.
byte expectedDc = (byte)((sum + (this.blockWidth >> 1)) / this.blockWidth);
for (uint r = 0; r < this.blockHeight; r++)
{
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone1Predictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone1Predictor.cs
index 86212dd09..662010c5d 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone1Predictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone1Predictor.cs
@@ -6,29 +6,66 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Produces 8-bit AV1 directional intra predictions for angles in zone 1.
+///
+///
+/// Zone 1 projects the top reference samples into the block for prediction angles less than 90 degrees.
+/// The scalar prediction follows the directional prediction process in section 7.11.2.4 of the AV1 specification.
+///
internal class Av1DirectionalZone1Predictor
{
+ ///
+ /// The width of the prediction block in samples.
+ ///
private readonly nuint blockWidth;
+
+ ///
+ /// The height of the prediction block in samples.
+ ///
private readonly nuint blockHeight;
+ ///
+ /// Initializes a new instance of the class for the specified block dimensions.
+ ///
+ /// The dimensions of the prediction block.
public Av1DirectionalZone1Predictor(Size blockSize)
{
this.blockWidth = (nuint)blockSize.Width;
this.blockHeight = (nuint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for the specified transform size.
+ ///
+ /// The transform size that determines the prediction block dimensions.
public Av1DirectionalZone1Predictor(Av1TransformSize transformSize)
{
this.blockWidth = (nuint)transformSize.GetWidth();
this.blockHeight = (nuint)transformSize.GetHeight();
}
+ ///
+ /// Produces an 8-bit zone 1 directional prediction for a transform block.
+ ///
+ /// The transform size that determines the prediction block dimensions.
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The top reference samples, including any required extension.
+ /// A value indicating whether the top reference samples were upsampled.
+ /// The horizontal projection derivative in Q6 precision.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, bool upsampleAbove, int dx)
=> new Av1DirectionalZone1Predictor(transformSize).PredictScalar(destination, stride, above, upsampleAbove, dx);
///
- /// SVT: svt_av1_dr_prediction_z1_c
+ /// Produces an 8-bit zone 1 directional prediction for this block.
///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The top reference samples, including any required extension.
+ /// A value indicating whether the top reference samples were upsampled.
+ /// The horizontal projection derivative in Q6 precision.
+ /// Corresponds to svt_av1_dr_prediction_z1_c in SVT-AV1.
public void PredictScalar(Span destination, nuint stride, Span above, bool upsample, int dx)
{
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
@@ -42,6 +79,8 @@ internal class Av1DirectionalZone1Predictor
int x = dx;
for (nuint r = 0; r < this.blockHeight; ++r)
{
+ // AV1 retains six fractional projection bits, or five after reference upsampling.
+ // The interpolation weights sum to 32 because the low projection bit is discarded.
int basis = x >> fractionBitCount, shift = ((x << upsampleAbove) & 0x3F) >> 1;
if (basis >= maxBasisX)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone2Predictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone2Predictor.cs
index 1c4e54863..74b25509b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone2Predictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone2Predictor.cs
@@ -8,29 +8,72 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Produces 8-bit AV1 directional intra predictions for angles in zone 2.
+///
+///
+/// Zone 2 projects both top and left reference samples into the block for prediction angles between 90 and 180 degrees.
+/// The scalar prediction follows the directional prediction process in section 7.11.2.4 of the AV1 specification.
+///
internal class Av1DirectionalZone2Predictor
{
+ ///
+ /// The width of the prediction block in samples.
+ ///
private readonly nuint blockWidth;
+
+ ///
+ /// The height of the prediction block in samples.
+ ///
private readonly nuint blockHeight;
+ ///
+ /// Initializes a new instance of the class for the specified block dimensions.
+ ///
+ /// The dimensions of the prediction block.
public Av1DirectionalZone2Predictor(Size blockSize)
{
this.blockWidth = (nuint)blockSize.Width;
this.blockHeight = (nuint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for the specified transform size.
+ ///
+ /// The transform size that determines the prediction block dimensions.
public Av1DirectionalZone2Predictor(Av1TransformSize transformSize)
{
this.blockWidth = (nuint)transformSize.GetWidth();
this.blockHeight = (nuint)transformSize.GetHeight();
}
+ ///
+ /// Produces an 8-bit zone 2 directional prediction for a transform block.
+ ///
+ /// The transform size that determines the prediction block dimensions.
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The top reference samples, including any required extension.
+ /// The left reference samples, including any required extension.
+ /// A value indicating whether the top reference samples were upsampled.
+ /// A value indicating whether the left reference samples were upsampled.
+ /// The horizontal projection derivative in Q6 precision.
+ /// The vertical projection derivative in Q6 precision.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left, bool upsampleAbove, bool upsampleLeft, int dx, int dy)
=> new Av1DirectionalZone2Predictor(transformSize).PredictScalar(destination, stride, above, left, upsampleAbove, upsampleLeft, dx, dy);
///
- /// SVT: svt_av1_dr_prediction_z1_c
+ /// Produces an 8-bit zone 2 directional prediction for this block.
///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The top reference samples, including any required extension.
+ /// The left reference samples, including any required extension.
+ /// A value indicating whether the top reference samples were upsampled.
+ /// A value indicating whether the left reference samples were upsampled.
+ /// The horizontal projection derivative in Q6 precision.
+ /// The vertical projection derivative in Q6 precision.
+ /// Corresponds to svt_av1_dr_prediction_z2_c in SVT-AV1.
public void PredictScalar(Span destination, nuint stride, Span above, Span left, bool doUpsampleAbove, bool doUpsampleLeft, int dx, int dy)
{
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
@@ -54,6 +97,8 @@ internal class Av1DirectionalZone2Predictor
int y = ((int)r << 6) - dy;
for (nuint c = 0; c < this.blockWidth; ++c, base1 += basisIncrementX, y -= dy)
{
+ // A nonnegative top projection uses the above edge. Once the projection crosses
+ // the top-left corner, the same destination sample is projected from the left edge.
if (base1 >= minBasisX)
{
int shift1 = ((x * (1 << upsampleAbove)) & 0x3F) >> 1;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone3Predictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone3Predictor.cs
index 71fde329c..99aedafee 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone3Predictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DirectionalZone3Predictor.cs
@@ -7,29 +7,68 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Produces 8-bit AV1 directional intra predictions for angles in zone 3.
+///
+///
+/// Zone 3 projects the left reference samples into the block for prediction angles greater than 180 degrees.
+/// The scalar prediction follows the directional prediction process in section 7.11.2.4 of the AV1 specification.
+///
internal class Av1DirectionalZone3Predictor
{
+ ///
+ /// The width of the prediction block in samples.
+ ///
private readonly nuint blockWidth;
+
+ ///
+ /// The height of the prediction block in samples.
+ ///
private readonly nuint blockHeight;
+ ///
+ /// Initializes a new instance of the class for the specified block dimensions.
+ ///
+ /// The dimensions of the prediction block.
public Av1DirectionalZone3Predictor(Size blockSize)
{
this.blockWidth = (nuint)blockSize.Width;
this.blockHeight = (nuint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for the specified transform size.
+ ///
+ /// The transform size that determines the prediction block dimensions.
public Av1DirectionalZone3Predictor(Av1TransformSize transformSize)
{
this.blockWidth = (nuint)transformSize.GetWidth();
this.blockHeight = (nuint)transformSize.GetHeight();
}
- public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span left, bool upsampleAbove, int dx, int dy)
- => new Av1DirectionalZone3Predictor(transformSize).PredictScalar(destination, stride, left, upsampleAbove, dx, dy);
+ ///
+ /// Produces an 8-bit zone 3 directional prediction for a transform block.
+ ///
+ /// The transform size that determines the prediction block dimensions.
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The left reference samples, including any required extension.
+ /// A value indicating whether the left reference samples were upsampled.
+ /// The horizontal projection derivative, which must be one in zone 3.
+ /// The vertical projection derivative in Q6 precision.
+ public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span left, bool upsampleLeft, int dx, int dy)
+ => new Av1DirectionalZone3Predictor(transformSize).PredictScalar(destination, stride, left, upsampleLeft, dx, dy);
///
- /// SVT: svt_av1_dr_prediction_z3_c
+ /// Produces an 8-bit zone 3 directional prediction for this block.
///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The left reference samples, including any required extension.
+ /// A value indicating whether the left reference samples were upsampled.
+ /// The horizontal projection derivative, which must be one in zone 3.
+ /// The vertical projection derivative in Q6 precision.
+ /// Corresponds to svt_av1_dr_prediction_z3_c in SVT-AV1.
public void PredictScalar(Span destination, nuint stride, Span left, bool upsample, int dx, int dy)
{
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
@@ -47,6 +86,8 @@ internal class Av1DirectionalZone3Predictor
int y = dy;
for (nuint c = 0; c < this.blockWidth; ++c)
{
+ // Zone 3 is the transpose of zone 1: columns advance along the projected left edge,
+ // while rows advance through the reference samples for each destination column.
int basis = y >> fractionBitCount;
int shift = ((y << upsampleLeft) & 0x3F) >> 1;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1FilterIntraPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1FilterIntraPredictor.cs
index c07d823ac..0f2217fa8 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1FilterIntraPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1FilterIntraPredictor.cs
@@ -8,17 +8,47 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Produces AV1 filter intra predictions from reconstructed neighboring samples.
+///
+///
+/// The scalar prediction follows the filter intra prediction process in section 7.11.2.3 of the AV1 specification.
+///
internal static class Av1FilterIntraPredictor
{
+ ///
+ /// The row stride of the temporary prediction buffer.
+ ///
private const int BufferStride = 33;
+
+ ///
+ /// The number of samples in the temporary prediction buffer.
+ ///
private const int BufferLength = BufferStride * BufferStride;
+
+ ///
+ /// The number of nonzero filter coefficients used to predict each sample.
+ ///
private const int TapsPerPixel = 7;
+
+ ///
+ /// The number of samples produced by each filter coefficient group.
+ ///
private const int PixelsPerGroup = 8;
+
+ ///
+ /// The number of stored coefficients for each filter intra mode.
+ ///
private const int TapsPerMode = TapsPerPixel * PixelsPerGroup;
- // AV1 7.11.2.3 defines five sets of eight filters over the same seven
- // already-reconstructed neighbors. The omitted eighth libaom tap is zero.
- internal static readonly sbyte[] Taps =
+ ///
+ /// Gets the filter coefficients for the five AV1 filter intra modes.
+ ///
+ ///
+ /// AV1 defines eight filters per mode over seven nonzero neighboring samples. The eighth coefficient used by
+ /// libaom is always zero, so it is omitted here to keep the scalar coefficient layout aligned with the work performed.
+ ///
+ public static readonly sbyte[] Taps =
[
// DC
@@ -72,7 +102,16 @@ internal static class Av1FilterIntraPredictor
-7, 0, 0, 1, 12, 1, 9,
];
- internal static void Predict(
+ ///
+ /// Produces an 8-bit filter intra prediction for a transform block.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The transform size that determines the prediction block dimensions.
+ /// The reconstructed top reference samples.
+ /// The reconstructed left reference samples.
+ /// The filter intra mode whose coefficient set is applied.
+ public static void Predict(
Span destination,
nuint destinationStride,
Av1TransformSize transformSize,
@@ -95,6 +134,8 @@ internal static class Av1FilterIntraPredictor
ref byte leftRef = ref left[0];
// Row zero includes the top-left sample followed by the top neighbors.
+ // Column zero stores the left neighbors so each 4-by-2 group can consume the
+ // seven already-reconstructed samples defined by the recursive AV1 process.
bufferRef = Unsafe.Subtract(ref aboveRef, 1);
above[..width].CopyTo(buffer[1..]);
for (int row = 0; row < height; row++)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1HighBitDepthPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1HighBitDepthPredictor.cs
index e2364d040..57b59f6a8 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1HighBitDepthPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1HighBitDepthPredictor.cs
@@ -10,15 +10,48 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
///
/// Implements AV1 intra prediction for 10-bit and 12-bit sample buffers.
///
+///
+/// Samples are stored in signed 16-bit buffers, but predictions are clamped to the nonnegative range of the signaled bit depth.
+///
internal static class Av1HighBitDepthPredictor
{
+ ///
+ /// The row stride of the temporary filter intra prediction buffer.
+ ///
private const int FilterBufferStride = 33;
+
+ ///
+ /// The number of samples in the temporary filter intra prediction buffer.
+ ///
private const int FilterBufferLength = FilterBufferStride * FilterBufferStride;
+
+ ///
+ /// The number of nonzero filter coefficients used to predict each filter intra sample.
+ ///
private const int FilterTapsPerPixel = 7;
+
+ ///
+ /// The number of samples produced by each filter coefficient group.
+ ///
private const int FilterPixelsPerGroup = 8;
+
+ ///
+ /// The number of stored coefficients for each filter intra mode.
+ ///
private const int FilterTapsPerMode = FilterTapsPerPixel * FilterPixelsPerGroup;
- internal static void DcPredictor(
+ ///
+ /// Produces a high-bit-depth DC intra prediction from the available neighboring samples.
+ ///
+ /// A value indicating whether reconstructed left samples are available.
+ /// A value indicating whether reconstructed top samples are available.
+ /// The transform size that determines the prediction block dimensions.
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The reconstructed top reference samples.
+ /// The reconstructed left reference samples.
+ /// The number of bits used to represent each sample.
+ public static void DcPredictor(
bool hasLeft,
bool hasAbove,
Av1TransformSize transformSize,
@@ -57,10 +90,21 @@ internal static class Av1HighBitDepthPredictor
? (short)(1 << (bitDepth - 1))
: (short)((sum + (count >> 1)) / count);
+ // The midpoint is normative when neither edge exists; otherwise the half-count bias
+ // rounds the mean of the available top and left samples to the nearest integer.
Fill(destination, destinationStride, width, height, prediction);
}
- internal static void GeneralPredictor(
+ ///
+ /// Produces a high-bit-depth nondirectional intra prediction.
+ ///
+ /// The nondirectional prediction mode to apply.
+ /// The transform size that determines the prediction block dimensions.
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The reconstructed top reference samples.
+ /// The reconstructed left reference samples.
+ public static void GeneralPredictor(
Av1PredictionMode mode,
Av1TransformSize transformSize,
Span destination,
@@ -94,7 +138,19 @@ internal static class Av1HighBitDepthPredictor
}
}
- internal static void DirectionalPredictor(
+ ///
+ /// Produces a high-bit-depth directional intra prediction.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The transform size that determines the prediction block dimensions.
+ /// The top reference samples, including any required extension.
+ /// The left reference samples, including any required extension.
+ /// A value indicating whether the top reference samples were upsampled.
+ /// A value indicating whether the left reference samples were upsampled.
+ /// The prediction angle in degrees.
+ /// The number of bits used to represent each sample.
+ public static void DirectionalPredictor(
Span destination,
nuint destinationStride,
Av1TransformSize transformSize,
@@ -111,6 +167,8 @@ internal static class Av1HighBitDepthPredictor
int width = transformSize.GetWidth();
int height = transformSize.GetHeight();
+ // Angles on a cardinal axis copy one reference edge directly. Other angles are
+ // separated into the three AV1 projection zones according to the edges they cross.
if (angle is > 0 and < 90)
{
PredictDirectionalZone1(destination, destinationStride, above, upsampleAbove, dx, width, height, bitDepth);
@@ -133,7 +191,17 @@ internal static class Av1HighBitDepthPredictor
}
}
- internal static void FilterIntraPredictor(
+ ///
+ /// Produces a high-bit-depth filter intra prediction.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The transform size that determines the prediction block dimensions.
+ /// The reconstructed top reference samples.
+ /// The reconstructed left reference samples.
+ /// The filter intra mode whose coefficient set is applied.
+ /// The number of bits used to represent each sample.
+ public static void FilterIntraPredictor(
Span destination,
nuint destinationStride,
Av1TransformSize transformSize,
@@ -158,6 +226,8 @@ internal static class Av1HighBitDepthPredictor
ref short leftRef = ref left[0];
// Row zero includes the top-left sample followed by the top neighbors.
+ // Column zero stores the left neighbors so each 4-by-2 group can consume the
+ // seven already-reconstructed samples defined by the recursive AV1 process.
bufferRef = Unsafe.Subtract(ref aboveRef, 1);
above[..width].CopyTo(buffer[1..]);
for (int row = 0; row < height; row++)
@@ -208,6 +278,14 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Copies each left reference sample across one destination row.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The reconstructed left reference samples.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
private static void PredictHorizontal(Span destination, nuint stride, Span left, int width, int height)
{
for (int row = 0; row < height; row++)
@@ -216,6 +294,14 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Copies the top reference samples into every destination row.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The reconstructed top reference samples.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
private static void PredictVertical(Span destination, nuint stride, Span above, int width, int height)
{
for (int row = 0; row < height; row++)
@@ -224,6 +310,15 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Produces a Paeth prediction from the nearest top, left, and top-left reference sample.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The reconstructed top reference samples.
+ /// The reconstructed left reference samples.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
private static void PredictPaeth(Span destination, nuint stride, Span above, Span left, int width, int height)
{
int topLeft = Unsafe.Subtract(ref above[0], 1);
@@ -238,6 +333,8 @@ internal static class Av1HighBitDepthPredictor
int leftDistance = Av1Math.AbsoluteDifference(basis, leftValue);
int topDistance = Av1Math.AbsoluteDifference(basis, topValue);
int topLeftDistance = Av1Math.AbsoluteDifference(basis, topLeft);
+
+ // The comparison order preserves AV1's left, top, then top-left tie precedence.
destinationRow[column] = leftDistance <= topDistance && leftDistance <= topLeftDistance
? leftValue
: topDistance <= topLeftDistance ? topValue : (short)topLeft;
@@ -245,6 +342,15 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Produces a two-dimensional smooth prediction from the four terminating edge samples.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The reconstructed top reference samples.
+ /// The reconstructed left reference samples.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
private static void PredictSmooth(Span destination, nuint stride, Span above, Span left, int width, int height)
{
int below = left[height - 1];
@@ -254,6 +360,8 @@ internal static class Av1HighBitDepthPredictor
int scale = 1 << Av1SmoothPredictor.WeightLog2Scale;
int log2Scale = Av1SmoothPredictor.WeightLog2Scale + 1;
+ // Horizontal weights follow the block width and vertical weights follow the height.
+ // Keeping those domains separate is required for rectangular transform blocks.
for (int row = 0; row < height; row++)
{
int rowWeight = heightWeights[row];
@@ -268,6 +376,15 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Produces a horizontal smooth prediction between the left and right edge samples.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The reconstructed top reference samples.
+ /// The reconstructed left reference samples.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
private static void PredictSmoothHorizontal(Span destination, nuint stride, Span above, Span left, int width, int height)
{
int right = above[width - 1];
@@ -286,6 +403,15 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Produces a vertical smooth prediction between the top and bottom edge samples.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The reconstructed top reference samples.
+ /// The reconstructed left reference samples.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
private static void PredictSmoothVertical(Span destination, nuint stride, Span above, Span left, int width, int height)
{
int below = left[height - 1];
@@ -304,6 +430,17 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Projects top reference samples into a directional zone 1 prediction block.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The top reference samples, including any required extension.
+ /// A value indicating whether the top reference samples were upsampled.
+ /// The horizontal projection derivative in Q6 precision.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
+ /// The number of bits used to represent each sample.
private static void PredictDirectionalZone1(Span destination, nuint stride, Span above, bool upsample, int dx, int width, int height, int bitDepth)
{
int upsampleAbove = upsample ? 1 : 0;
@@ -317,6 +454,9 @@ internal static class Av1HighBitDepthPredictor
for (int row = 0; row < height; row++)
{
Span destinationRow = destination.Slice(row * (int)stride, width);
+
+ // AV1 retains six fractional projection bits, or five after reference upsampling.
+ // The interpolation weights sum to 32 because the low projection bit is discarded.
int basis = x >> fractionBitCount;
int shift = ((x << upsampleAbove) & 0x3F) >> 1;
for (int column = 0; column < width; column++)
@@ -338,6 +478,20 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Projects top and left reference samples into a directional zone 2 prediction block.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The top reference samples, including any required extension.
+ /// The left reference samples, including any required extension.
+ /// A value indicating whether the top reference samples were upsampled.
+ /// A value indicating whether the left reference samples were upsampled.
+ /// The horizontal projection derivative in Q6 precision.
+ /// The vertical projection derivative in Q6 precision.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
+ /// The number of bits used to represent each sample.
private static void PredictDirectionalZone2(Span destination, nuint stride, Span above, Span left, bool doUpsampleAbove, bool doUpsampleLeft, int dx, int dy, int width, int height, int bitDepth)
{
int upsampleAbove = doUpsampleAbove ? 1 : 0;
@@ -359,6 +513,9 @@ internal static class Av1HighBitDepthPredictor
for (int column = 0; column < width; column++, basisX += basisIncrementX, y -= dy)
{
int prediction;
+
+ // A nonnegative top projection uses the above edge. Once the projection crosses
+ // the top-left corner, the same destination sample is projected from the left edge.
if (basisX >= minBasisX)
{
int shift = ((x * (1 << upsampleAbove)) & 0x3F) >> 1;
@@ -378,6 +535,18 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Projects left reference samples into a directional zone 3 prediction block.
+ ///
+ /// The buffer that receives the predicted samples.
+ /// The distance, in samples, between destination rows.
+ /// The left reference samples, including any required extension.
+ /// A value indicating whether the left reference samples were upsampled.
+ /// The horizontal projection derivative, which must be one in zone 3.
+ /// The vertical projection derivative in Q6 precision.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
+ /// The number of bits used to represent each sample.
private static void PredictDirectionalZone3(Span destination, nuint stride, Span left, bool upsample, int dx, int dy, int width, int height, int bitDepth)
{
int upsampleLeft = upsample ? 1 : 0;
@@ -391,6 +560,8 @@ internal static class Av1HighBitDepthPredictor
for (int column = 0; column < width; column++)
{
+ // Zone 3 is the transpose of zone 1: columns advance along the projected left edge,
+ // while rows advance through the reference samples for each destination column.
int basis = y >> fractionBitCount;
int shift = ((y << upsampleLeft) & 0x3F) >> 1;
for (int row = 0; row < height; row++)
@@ -413,6 +584,14 @@ internal static class Av1HighBitDepthPredictor
}
}
+ ///
+ /// Fills a rectangular prediction block with one sample value.
+ ///
+ /// The buffer that receives the sample value.
+ /// The distance, in samples, between destination rows.
+ /// The width of the prediction block in samples.
+ /// The height of the prediction block in samples.
+ /// The sample value written to every destination position.
private static void Fill(Span destination, nuint stride, int width, int height, short value)
{
for (int row = 0; row < height; row++)
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1HorizontalPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1HorizontalPredictor.cs
index 75f96f22b..f84e0abe3 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1HorizontalPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1HorizontalPredictor.cs
@@ -6,29 +6,54 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block by extending each left neighboring sample across its destination row.
+///
internal class Av1HorizontalPredictor : IAv1Predictor
{
+ ///
+ /// The number of samples written to each destination row.
+ ///
private readonly nuint blockWidth;
+
+ ///
+ /// The number of left samples consumed and destination rows written.
+ ///
private readonly nuint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1HorizontalPredictor(Size blockSize)
{
this.blockWidth = (nuint)blockSize.Width;
this.blockHeight = (nuint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1HorizontalPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (nuint)transformSize.GetWidth();
this.blockHeight = (nuint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block by extending its left edge horizontally.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The unused top-neighbor buffer required by the common predictor signature.
+ /// The left neighboring samples.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1HorizontalPredictor(transformSize).PredictScalar(destination, stride, above, left);
- ///
- /// SVT: highbd_h_predictor
- ///
+ ///
+ /// SVT-AV1: highbd_h_predictor.
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1NeighborNeed.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1NeighborNeed.cs
index 81408fad3..3dba6f76c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1NeighborNeed.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1NeighborNeed.cs
@@ -3,13 +3,39 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Identifies the neighboring sample regions required by an AV1 intra-prediction mode.
+///
[Flags]
internal enum Av1NeighborNeed
{
+ ///
+ /// No neighboring samples are required.
+ ///
Nothing = 0,
+
+ ///
+ /// Samples immediately left of the block are required.
+ ///
Left = 2,
+
+ ///
+ /// Samples immediately above the block are required.
+ ///
Above = 4,
+
+ ///
+ /// Samples extending right of the top edge are required.
+ ///
AboveRight = 8,
+
+ ///
+ /// The sample diagonally above and left of the block is required.
+ ///
AboveLeft = 16,
+
+ ///
+ /// Samples extending below the left edge are required.
+ ///
BottomLeft = 32,
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PaethPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PaethPredictor.cs
index 096cd9b21..4efb40acc 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PaethPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PaethPredictor.cs
@@ -6,26 +6,53 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block by selecting the top, left, or top-left neighbor with the smallest local gradient.
+///
internal class Av1PaethPredictor : IAv1Predictor
{
+ ///
+ /// The number of top samples consumed and samples written to each destination row.
+ ///
private readonly uint blockWidth;
+
+ ///
+ /// The number of left samples consumed and destination rows written.
+ ///
private readonly uint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1PaethPredictor(Size blockSize)
{
this.blockWidth = (uint)blockSize.Width;
this.blockHeight = (uint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1PaethPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (uint)transformSize.GetWidth();
this.blockHeight = (uint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block using the Paeth gradient selector.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples, preceded in memory by the top-left sample.
+ /// The left neighboring samples.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1PaethPredictor(transformSize).PredictScalar(destination, stride, above, left);
+ ///
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
@@ -52,6 +79,13 @@ internal class Av1PaethPredictor : IAv1Predictor
}
}
+ ///
+ /// Selects the reference sample nearest to the planar estimate + - .
+ ///
+ /// The left reference sample.
+ /// The top reference sample.
+ /// The shared top-left reference sample.
+ /// The reference sample with the smallest absolute distance from the planar estimate.
private static byte PredictSingle(byte left, byte top, int topLeft)
{
int basis = top + left - topLeft;
@@ -59,7 +93,7 @@ internal class Av1PaethPredictor : IAv1Predictor
int pTop = Av1Math.AbsoluteDifference(basis, top);
int pTopLeft = Av1Math.AbsoluteDifference(basis, topLeft);
- // Return nearest to base of left, top and top_left.
+ // Ties prefer left, then top, matching AV1's normative Paeth selection order.
return (byte)((pLeft <= pTop && pLeft <= pTopLeft) ? left : (pTop <= pTopLeft) ? top : topLeft);
}
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs
index 86d9e96c7..d9e0a4805 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs
@@ -3,26 +3,104 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
-// Inter modes are not defined here, as they do not apply to pictures.
+///
+/// Identifies the intra-prediction modes used by an AV1 still-picture frame.
+///
+/// Inter modes are omitted because reduced still-picture frames do not reference other frames.
internal enum Av1PredictionMode
{
+ ///
+ /// Predicts each sample from the average of the available top and left neighbors.
+ ///
DC,
+
+ ///
+ /// Repeats the top neighboring row vertically through the block.
+ ///
Vertical,
+
+ ///
+ /// Repeats the left neighboring column horizontally through the block.
+ ///
Horizontal,
+
+ ///
+ /// Projects neighboring samples into the block at 45 degrees.
+ ///
Directional45Degrees,
+
+ ///
+ /// Projects neighboring samples into the block at 135 degrees.
+ ///
Directional135Degrees,
+
+ ///
+ /// Projects neighboring samples into the block at 113 degrees.
+ ///
Directional113Degrees,
+
+ ///
+ /// Projects neighboring samples into the block at 157 degrees.
+ ///
Directional157Degrees,
+
+ ///
+ /// Projects neighboring samples into the block at 203 degrees.
+ ///
Directional203Degrees,
+
+ ///
+ /// Projects neighboring samples into the block at 67 degrees.
+ ///
Directional67Degrees,
+
+ ///
+ /// Blends horizontal and vertical smooth predictions.
+ ///
Smooth,
+
+ ///
+ /// Interpolates vertically between the top row and the bottom-left neighbor.
+ ///
SmoothVertical,
+
+ ///
+ /// Interpolates horizontally between the left column and the top-right neighbor.
+ ///
SmoothHorizontal,
+
+ ///
+ /// Selects the neighbor with the smallest gradient from the top-left reference.
+ ///
Paeth,
+
+ ///
+ /// Predicts chroma from the reconstructed luma AC surface.
+ ///
UvChromaFromLuma,
+
+ ///
+ /// The first luma intra-prediction mode.
+ ///
IntraModeStart = DC,
+
+ ///
+ /// The exclusive upper bound of luma intra-prediction modes.
+ ///
IntraModeEnd = Paeth + 1,
- IntraModes = Paeth,
+
+ ///
+ /// The number of luma intra-prediction modes.
+ ///
+ IntraModes = Paeth + 1,
+
+ ///
+ /// The number of chroma intra-prediction modes, including chroma-from-luma.
+ ///
UvIntraModes = UvChromaFromLuma + 1,
+
+ ///
+ /// The invalid intra-mode sentinel matching the complete AV1 prediction-mode domain.
+ ///
IntraInvalid = 25,
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictorFactory.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictorFactory.cs
index 496a0dbb9..c64d99c1d 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictorFactory.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictorFactory.cs
@@ -6,8 +6,14 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Selects the scalar 8-bit or high-bit-depth AV1 intra predictor for a decoded prediction mode.
+///
internal class Av1PredictorFactory
{
+ ///
+ /// The Q8 directional derivatives indexed by acute angle in degrees; zero entries represent angles AV1 does not signal.
+ ///
private static readonly int[] DirectionalIntraDerivative = [
// More evenly spread out angles and limited to 10-bit
@@ -43,7 +49,17 @@ internal class Av1PredictorFactory
3, 0, 0, // 87, ...
];
- internal static void DcPredictor(bool hasLeft, bool hasAbove, Av1TransformSize transformSize, Span destination, nuint destinationStride, Span aboveRow, Span leftColumn)
+ ///
+ /// Predicts an 8-bit block from the average of whichever top and left neighbor edges are available.
+ ///
+ /// Whether the left neighboring column is available.
+ /// Whether the top neighboring row is available.
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples.
+ /// The left neighboring samples.
+ public static void DcPredictor(bool hasLeft, bool hasAbove, Av1TransformSize transformSize, Span destination, nuint destinationStride, Span aboveRow, Span leftColumn)
{
if (hasLeft)
{
@@ -69,13 +85,33 @@ internal class Av1PredictorFactory
}
}
- internal static void DcPredictor(bool hasLeft, bool hasAbove, Av1TransformSize transformSize, Span destination, nuint destinationStride, Span aboveRow, Span leftColumn, int bitDepth)
+ ///
+ /// Predicts a high-bit-depth block from the average of whichever top and left neighbor edges are available.
+ ///
+ /// Whether the left neighboring column is available.
+ /// Whether the top neighboring row is available.
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples.
+ /// The left neighboring samples.
+ /// The coded sample bit depth used to select the midpoint when no edge is available.
+ public static void DcPredictor(bool hasLeft, bool hasAbove, Av1TransformSize transformSize, Span destination, nuint destinationStride, Span aboveRow, Span leftColumn, int bitDepth)
=> Av1HighBitDepthPredictor.DcPredictor(hasLeft, hasAbove, transformSize, destination, destinationStride, aboveRow, leftColumn, bitDepth);
///
- /// SVT: svt_aom_highbd_dr_predictor
+ /// Predicts an 8-bit block by projecting reference-edge samples along a directional angle.
///
- internal static void DirectionalPredictor(Span destination, nuint stride, Av1TransformSize transformSize, Span aboveRow, Span leftColumn, bool upsampleAbove, bool upsampleLeft, int angle)
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The predicted block dimensions.
+ /// The top and top-right reference samples.
+ /// The left and bottom-left reference samples.
+ /// Whether the top reference edge is stored at half-sample intervals.
+ /// Whether the left reference edge is stored at half-sample intervals.
+ /// The prediction angle in degrees from 1 through 269.
+ /// SVT-AV1: svt_aom_highbd_dr_predictor.
+ public static void DirectionalPredictor(Span destination, nuint stride, Av1TransformSize transformSize, Span aboveRow, Span leftColumn, bool upsampleAbove, bool upsampleLeft, int angle)
{
int dx = GetDeltaX(angle);
int dy = GetDeltaY(angle);
@@ -83,6 +119,8 @@ internal class Av1PredictorFactory
int bh = transformSize.GetHeight();
Guard.MustBeBetweenOrEqualTo(angle, 1, 269, nameof(angle));
+ // The three open quadrants select which reference edge or edge pair the projected ray intersects.
+ // Exact 90- and 180-degree modes reduce to copying the top row or left column without interpolation.
if (angle is > 0 and < 90)
{
Av1DirectionalZone1Predictor.PredictScalar(transformSize, destination, stride, aboveRow, upsampleAbove, dx);
@@ -105,16 +143,56 @@ internal class Av1PredictorFactory
}
}
- internal static void DirectionalPredictor(Span destination, nuint stride, Av1TransformSize transformSize, Span aboveRow, Span leftColumn, bool upsampleAbove, bool upsampleLeft, int angle, int bitDepth)
+ ///
+ /// Predicts a high-bit-depth block by projecting reference-edge samples along a directional angle.
+ ///
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The predicted block dimensions.
+ /// The top and top-right reference samples.
+ /// The left and bottom-left reference samples.
+ /// Whether the top reference edge is stored at half-sample intervals.
+ /// Whether the left reference edge is stored at half-sample intervals.
+ /// The prediction angle in degrees from 1 through 269.
+ /// The coded sample bit depth used to clamp interpolated values.
+ public static void DirectionalPredictor(Span destination, nuint stride, Av1TransformSize transformSize, Span aboveRow, Span leftColumn, bool upsampleAbove, bool upsampleLeft, int angle, int bitDepth)
=> Av1HighBitDepthPredictor.DirectionalPredictor(destination, stride, transformSize, aboveRow, leftColumn, upsampleAbove, upsampleLeft, angle, bitDepth);
- internal static void FilterIntraPredictor(Span destination, nuint destinationStride, Av1TransformSize transformSize, Span aboveRow, Span leftColumn, Av1FilterIntraMode filterIntraMode)
+ ///
+ /// Predicts an 8-bit block using the selected AV1 filter-intra kernel.
+ ///
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The predicted block dimensions.
+ /// The top neighboring samples.
+ /// The left neighboring samples.
+ /// The filter-intra coefficient set.
+ public static void FilterIntraPredictor(Span destination, nuint destinationStride, Av1TransformSize transformSize, Span aboveRow, Span leftColumn, Av1FilterIntraMode filterIntraMode)
=> Av1FilterIntraPredictor.Predict(destination, destinationStride, transformSize, aboveRow, leftColumn, filterIntraMode);
- internal static void FilterIntraPredictor(Span destination, nuint destinationStride, Av1TransformSize transformSize, Span aboveRow, Span leftColumn, Av1FilterIntraMode filterIntraMode, int bitDepth)
+ ///
+ /// Predicts a high-bit-depth block using the selected AV1 filter-intra kernel.
+ ///
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The predicted block dimensions.
+ /// The top neighboring samples.
+ /// The left neighboring samples.
+ /// The filter-intra coefficient set.
+ /// The coded sample bit depth used to clamp filtered values.
+ public static void FilterIntraPredictor(Span destination, nuint destinationStride, Av1TransformSize transformSize, Span aboveRow, Span leftColumn, Av1FilterIntraMode filterIntraMode, int bitDepth)
=> Av1HighBitDepthPredictor.FilterIntraPredictor(destination, destinationStride, transformSize, aboveRow, leftColumn, filterIntraMode, bitDepth);
- internal static void GeneralPredictor(Av1PredictionMode mode, Av1TransformSize transformSize, Span destination, nuint destinationStride, Span aboveRow, Span leftColumn)
+ ///
+ /// Selects an 8-bit horizontal, vertical, Paeth, or smooth predictor for a non-directional mode.
+ ///
+ /// The non-directional prediction mode.
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples.
+ /// The left neighboring samples.
+ public static void GeneralPredictor(Av1PredictionMode mode, Av1TransformSize transformSize, Span destination, nuint destinationStride, Span aboveRow, Span leftColumn)
{
switch (mode)
{
@@ -139,14 +217,24 @@ internal class Av1PredictorFactory
}
}
- internal static void GeneralPredictor(Av1PredictionMode mode, Av1TransformSize transformSize, Span destination, nuint destinationStride, Span aboveRow, Span leftColumn)
+ ///
+ /// Selects a high-bit-depth horizontal, vertical, Paeth, or smooth predictor for a non-directional mode.
+ ///
+ /// The non-directional prediction mode.
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples.
+ /// The left neighboring samples.
+ public static void GeneralPredictor(Av1PredictionMode mode, Av1TransformSize transformSize, Span destination, nuint destinationStride, Span aboveRow, Span leftColumn)
=> Av1HighBitDepthPredictor.GeneralPredictor(mode, transformSize, destination, destinationStride, aboveRow, leftColumn);
- // Get the shift (up-scaled by 256) in Y w.r.t a unit change in X.
- // If angle > 0 && angle < 90, dy = 1;
- // If angle > 90 && angle < 180, dy = (int32_t)(256 * t);
- // If angle > 180 && angle < 270, dy = -((int32_t)(256 * t));
- internal static int GetDeltaY(int angle)
+ ///
+ /// Gets the Q8 vertical displacement per unit horizontal displacement for a directional angle.
+ ///
+ /// The prediction angle in degrees.
+ /// The Q8 vertical derivative, or one when the selected directional zone does not consume it.
+ public static int GetDeltaY(int angle)
{
if (angle is > 90 and < 180)
{
@@ -158,16 +246,17 @@ internal class Av1PredictorFactory
}
else
{
- // In this case, we are not really going to use dy. We may return any value.
+ // Zones one and the exact horizontal/vertical modes never consume dy; one avoids a zero placeholder.
return 1;
}
}
- // Get the shift (up-scaled by 256) in X w.r.t a unit change in Y.
- // If angle > 0 && angle < 90, dx = -((int32_t)(256 / t));
- // If angle > 90 && angle < 180, dx = (int32_t)(256 / t);
- // If angle > 180 && angle < 270, dx = 1;
- internal static int GetDeltaX(int angle)
+ ///
+ /// Gets the Q8 horizontal displacement per unit vertical displacement for a directional angle.
+ ///
+ /// The prediction angle in degrees.
+ /// The Q8 horizontal derivative, or one when the selected directional zone does not consume it.
+ public static int GetDeltaX(int angle)
{
if (angle is > 0 and < 90)
{
@@ -179,7 +268,7 @@ internal class Av1PredictorFactory
}
else
{
- // In this case, we are not really going to use dx. We may return any value.
+ // Zone three and the exact horizontal/vertical modes never consume dx; one avoids a zero placeholder.
return 1;
}
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PreditionModeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PreditionModeExtensions.cs
index 2b2ca3e4f..118a2cb23 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PreditionModeExtensions.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PreditionModeExtensions.cs
@@ -6,8 +6,14 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Provides transform, direction, angle, and neighbor metadata for AV1 intra-prediction modes.
+///
internal static class Av1PreditionModeExtensions
{
+ ///
+ /// Maps each luma intra-prediction mode to its default two-dimensional transform type.
+ ///
private static readonly Av1TransformType[] IntraPreditionMode2TransformType = [
Av1TransformType.DctDct, // DC
Av1TransformType.AdstDct, // V
@@ -24,6 +30,9 @@ internal static class Av1PreditionModeExtensions
Av1TransformType.AdstAdst, // PAETH
];
+ ///
+ /// Maps each luma intra-prediction mode to the neighboring sample regions it consumes.
+ ///
private static readonly Av1NeighborNeed[] NeedsMap = [
Av1NeighborNeed.Above | Av1NeighborNeed.Left, // DC
Av1NeighborNeed.Above, // V
@@ -40,6 +49,9 @@ internal static class Av1PreditionModeExtensions
Av1NeighborNeed.Left | Av1NeighborNeed.Above | Av1NeighborNeed.AboveLeft, // PAETH
];
+ ///
+ /// Maps each luma intra-prediction mode to its base directional angle in degrees, or zero for non-directional modes.
+ ///
private static readonly int[] AngleMap = [
0,
90,
@@ -56,12 +68,32 @@ internal static class Av1PreditionModeExtensions
0,
];
+ ///
+ /// Gets the default transform type associated with an intra-prediction mode.
+ ///
+ /// The luma intra-prediction mode.
+ /// The default transform type.
public static Av1TransformType ToTransformType(this Av1PredictionMode mode) => IntraPreditionMode2TransformType[(int)mode];
+ ///
+ /// Determines whether an intra-prediction mode projects samples along a coded angle.
+ ///
+ /// The luma intra-prediction mode.
+ /// for a directional mode; otherwise, .
public static bool IsDirectional(this Av1PredictionMode mode)
=> mode is >= Av1PredictionMode.Vertical and <= Av1PredictionMode.Directional67Degrees;
+ ///
+ /// Gets the neighboring sample regions required by an intra-prediction mode.
+ ///
+ /// The luma intra-prediction mode.
+ /// The required neighboring sample flags.
public static Av1NeighborNeed GetNeighborNeed(this Av1PredictionMode mode) => NeedsMap[(int)mode];
+ ///
+ /// Gets the base prediction angle for an intra-prediction mode.
+ ///
+ /// The luma intra-prediction mode.
+ /// The prediction angle in degrees, or zero for a non-directional mode.
public static int ToAngle(this Av1PredictionMode mode) => AngleMap[(int)mode];
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothHorizontalPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothHorizontalPredictor.cs
index 2666bce32..d0901e1a4 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothHorizontalPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothHorizontalPredictor.cs
@@ -6,29 +6,54 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block by smoothly blending each left neighbor toward the top-right reference sample.
+///
internal class Av1SmoothHorizontalPredictor : IAv1Predictor
{
+ ///
+ /// The number of interpolation weights and samples written to each destination row.
+ ///
private readonly nuint blockWidth;
+
+ ///
+ /// The number of left samples consumed and destination rows written.
+ ///
private readonly nuint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1SmoothHorizontalPredictor(Size blockSize)
{
this.blockWidth = (nuint)blockSize.Width;
this.blockHeight = (nuint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1SmoothHorizontalPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (nuint)transformSize.GetWidth();
this.blockHeight = (nuint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block using horizontal smooth interpolation.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples whose final value supplies the right endpoint.
+ /// The left neighboring samples.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1SmoothHorizontalPredictor(transformSize).PredictScalar(destination, stride, above, left);
- ///
- /// SVT: highbd_smooth_h_predictor
- ///
+ ///
+ /// SVT-AV1: highbd_smooth_h_predictor.
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
@@ -51,6 +76,8 @@ internal class Av1SmoothHorizontalPredictor : IAv1Predictor
{
int columnWeight = Unsafe.Add(ref weights, c);
Guard.MustBeGreaterThanOrEqualTo(scale, columnWeight, nameof(scale));
+
+ // The Q8 weight decreases toward the right edge, shifting influence from left to top-right.
int thisPredition = Unsafe.Add(ref leftRef, r) * columnWeight;
thisPredition += rightPrediction * (scale - columnWeight);
Unsafe.Add(ref destinationRef, c) = (byte)Av1Math.DivideRound(thisPredition, log2Scale);
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothPredictor.cs
index 796dd3e3a..5386803b2 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothPredictor.cs
@@ -6,12 +6,22 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block by blending top-to-bottom and left-to-right smooth interpolation surfaces.
+///
internal class Av1SmoothPredictor : IAv1Predictor
{
// Weights are quadratic from '1' to '1 / BlockSize', scaled by
// 2^sm_weight_log2_scale.
+
+ ///
+ /// The number of fractional bits in the normative smooth-prediction weights.
+ ///
internal static readonly int WeightLog2Scale = 8;
+ ///
+ /// The concatenated smooth-weight sequences, addressed by using the block dimension as the sequence offset.
+ ///
internal static readonly int[] Weights = [
// Unused, because we always offset by bs, which is at least 2.
@@ -40,27 +50,49 @@ internal class Av1SmoothPredictor : IAv1Predictor
13, 12, 10, 9, 8, 7, 6, 6, 5, 5, 4, 4, 4,
];
+ ///
+ /// The number of top samples consumed and samples written to each destination row.
+ ///
private readonly nuint blockWidth;
+
+ ///
+ /// The number of left samples consumed and destination rows written.
+ ///
private readonly nuint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1SmoothPredictor(Size blockSize)
{
this.blockWidth = (nuint)blockSize.Width;
this.blockHeight = (nuint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1SmoothPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (nuint)transformSize.GetWidth();
this.blockHeight = (nuint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block by combining horizontal and vertical smooth interpolation.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples.
+ /// The left neighboring samples.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1SmoothPredictor(transformSize).PredictScalar(destination, stride, above, left);
- ///
- /// SVT: highbd_smooth_predictor
- ///
+ ///
+ /// SVT-AV1: highbd_smooth_predictor.
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
@@ -72,10 +104,10 @@ internal class Av1SmoothPredictor : IAv1Predictor
ref byte destinationRef = ref destination[0];
int belowPrediction = Unsafe.Add(ref leftRef, this.blockHeight - 1); // estimated by bottom-left pixel
int rightPrediction = Unsafe.Add(ref aboveRef, this.blockWidth - 1); // estimated by top-right pixel
- ref int heightWeights = ref Weights[(int)this.blockWidth];
- ref int widthWeights = ref Weights[(int)this.blockHeight];
+ ref int heightWeights = ref Weights[(int)this.blockHeight];
+ ref int widthWeights = ref Weights[(int)this.blockWidth];
- // scale = 2 * 2^sm_weight_log2_scale
+ // The two independent interpolation surfaces each use Q8 weights, so their combined sum has one extra scale bit.
int log2Scale = 1 + WeightLog2Scale;
int scale = 1 << WeightLog2Scale;
@@ -88,6 +120,8 @@ internal class Av1SmoothPredictor : IAv1Predictor
{
int columnWeight = Unsafe.Add(ref widthWeights, c);
Guard.MustBeGreaterThanOrEqualTo(scale, columnWeight, nameof(scale));
+
+ // Blend top toward bottom-left and left toward top-right, then normalize their combined Q8 contributions.
int thisPredition = Unsafe.Add(ref aboveRef, c) * rowWeight;
thisPredition += belowPrediction * (scale - rowWeight);
thisPredition += Unsafe.Add(ref leftRef, r) * columnWeight;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothVerticalPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothVerticalPredictor.cs
index 265b2ab0c..bf618cf4b 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothVerticalPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1SmoothVerticalPredictor.cs
@@ -6,29 +6,54 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block by smoothly blending each top neighbor toward the bottom-left reference sample.
+///
internal class Av1SmoothVerticalPredictor : IAv1Predictor
{
+ ///
+ /// The number of top samples consumed and samples written to each destination row.
+ ///
private readonly nuint blockWidth;
+
+ ///
+ /// The number of interpolation weights, left samples consumed, and destination rows written.
+ ///
private readonly nuint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1SmoothVerticalPredictor(Size blockSize)
{
this.blockWidth = (nuint)blockSize.Width;
this.blockHeight = (nuint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1SmoothVerticalPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (nuint)transformSize.GetWidth();
this.blockHeight = (nuint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block using vertical smooth interpolation.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples.
+ /// The left neighboring samples whose final value supplies the bottom endpoint.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1SmoothVerticalPredictor(transformSize).PredictScalar(destination, stride, above, left);
- ///
- /// SVT: highbd_smooth_v_predictor
- ///
+ ///
+ /// SVT-AV1: highbd_smooth_v_predictor.
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
@@ -50,6 +75,7 @@ internal class Av1SmoothVerticalPredictor : IAv1Predictor
int rowWeight = Unsafe.Add(ref weights, r);
for (nuint c = 0; c < this.blockWidth; ++c)
{
+ // The Q8 weight decreases toward the bottom edge, shifting influence from top to bottom-left.
int thisPredition = Unsafe.Add(ref aboveRef, c) * rowWeight;
thisPredition += belowPrediction * (scale - rowWeight);
Unsafe.Add(ref destinationRef, c) = (byte)Av1Math.DivideRound(thisPredition, log2Scale);
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1VerticalPredictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1VerticalPredictor.cs
index ea23c04bc..b9cf9d192 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1VerticalPredictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1VerticalPredictor.cs
@@ -6,29 +6,54 @@ using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
+///
+/// Predicts an 8-bit AV1 block by copying the top neighboring samples into every destination row.
+///
internal class Av1VerticalPredictor : IAv1Predictor
{
+ ///
+ /// The number of top samples consumed and samples written to each destination row.
+ ///
private readonly nuint blockWidth;
+
+ ///
+ /// The number of destination rows.
+ ///
private readonly nuint blockHeight;
+ ///
+ /// Initializes a new instance of the class for explicit block dimensions.
+ ///
+ /// The predicted block dimensions in samples.
public Av1VerticalPredictor(Size blockSize)
{
this.blockWidth = (nuint)blockSize.Width;
this.blockHeight = (nuint)blockSize.Height;
}
+ ///
+ /// Initializes a new instance of the class for a transform size.
+ ///
+ /// The transform size whose dimensions define the predicted block.
public Av1VerticalPredictor(Av1TransformSize transformSize)
{
this.blockWidth = (nuint)transformSize.GetWidth();
this.blockHeight = (nuint)transformSize.GetHeight();
}
+ ///
+ /// Predicts a transform block by extending its top edge vertically.
+ ///
+ /// The predicted block dimensions.
+ /// The destination block.
+ /// The distance, in samples, between destination rows.
+ /// The top neighboring samples.
+ /// The unused left-neighbor buffer required by the common predictor signature.
public static void PredictScalar(Av1TransformSize transformSize, Span destination, nuint stride, Span above, Span left)
=> new Av1VerticalPredictor(transformSize).PredictScalar(destination, stride, above, left);
- ///
- /// SVT: highbd_v_predictor
- ///
+ ///
+ /// SVT-AV1: highbd_v_predictor.
public void PredictScalar(Span destination, nuint stride, Span above, Span left)
{
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride));
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/IAv1Predictor.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/IAv1Predictor.cs
index 567e6c652..c9c468998 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/IAv1Predictor.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/IAv1Predictor.cs
@@ -4,16 +4,16 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
///
-/// Interface for predictor implementations.
+/// Defines scalar reconstruction of an 8-bit AV1 intra-prediction block from its neighboring samples.
///
internal interface IAv1Predictor
{
///
- /// Predict using scalar logic within the 8-bit pipeline.
+ /// Writes the predicted block using scalar 8-bit arithmetic.
///
- /// The destination to write to.
- /// The stride of the destination buffer.
- /// Pointer to the first element of the block above.
- /// Pointer to the first element of the block to the left.
+ /// The destination block, starting at its top-left sample.
+ /// The distance, in samples, between destination rows.
+ /// The neighboring samples immediately above the block.
+ /// The neighboring samples immediately left of the block.
public void PredictScalar(Span destination, nuint stride, Span above, Span left);
}
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1PredictorTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1PredictorTests.cs
index f69656654..68573e6b4 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1PredictorTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1PredictorTests.cs
@@ -32,7 +32,7 @@ public class Av1PredictorTests
"7896fdbbfe538dce1dc3a5b0873d74b0", "504aea29c6b27f21555d5516b8de2d8a",
"c5738e7fa82b91ea0e39232120da56ea", "19abbd934c243a6d9df7585d81332dd5",
"9e42b7b342e45c842dfa8aedaddbdfaa", "0e9eb07a89f8bf96bc219d5d1c3d9f6d",
- "659393c31633e0f498bae384c9df5c7b", "29241129BC91B354D04CA6C09A7CF1E1",
+ "659393c31633e0f498bae384c9df5c7b", "812DFE5C38F2B837529C5A918756E8B8",
];
private static string[] Digests8x4 = [
@@ -56,7 +56,7 @@ public class Av1PredictorTests
"7911e2e02abfbe226f17529ac5db08fc", "064e509948982f66a14293f406d88d42",
"5c443aa713891406d5be3af4b3cf67c6", "5d2cb98e532822ca701110cda9ada968",
"3d58836e17918b8890012dd96b95bb9d", "20e8d61ddc451b9e553a294073349ffd",
- "a9aa6cf9d0dcf1977a1853ccc264e40b", "FEDA9D1554325ED2A243FC3DD6ADD4E3",
+ "a9aa6cf9d0dcf1977a1853ccc264e40b", "D84707DAAF6CA99F041A670F16885CBA",
];
private static string[] Digests8x32 = [
@@ -64,7 +64,7 @@ public class Av1PredictorTests
"f337dce3980f70730d6f6c2c756e3b62", "796189b05dc026e865c9e95491b255d1",
"ea932c21e7189eeb215c1990491320ab", "a9fffdf9455eba5e3b01317cae140289",
"9525dbfdbf5fba61ef9c7aa5fe887503", "8c6a7e3717ff8a459f415c79bb17341c",
- "3761071bfaa2363a315fe07223f95a2d", "506D691319D1AEF38DF5C1E060408BDD",
+ "3761071bfaa2363a315fe07223f95a2d", "25DADBA40EBC6C4616A7A536AC48CAA6",
];
private static string[] Digests16x4 = [
@@ -72,7 +72,7 @@ public class Av1PredictorTests
"28a6af15e31f76d3ff189012475d78f5", "e330d67b859bceef62b96fc9e1f49a34",
"36eca3b8083ce2fb5f7e6227dfc34e71", "08f567d2abaa8e83e4d9b33b3f709538",
"dc2d0ba13aa9369446932f03b53dc77d", "9ab342944c4b1357aa79d39d7bebdd3a",
- "77ec278c5086c88b91d68eef561ed517", "7B8ECDFBF449908E9C96664582C05BFE",
+ "77ec278c5086c88b91d68eef561ed517", "E4726C9838383FAC75A028303808EABF",
];
private static string[] Digests16x8 = [
@@ -80,7 +80,7 @@ public class Av1PredictorTests
"728d61c11b06baf7fe77881003a918b9", "889997b89a44c9976cb34f573e2b1eea",
"b43bfc31d1c770bb9ca5ca158c9beec4", "9d3fe9f762e0c6e4f114042147c50c7f",
"c74fdd7c9938603b01e7ecf9fdf08d61", "870c7336db1102f80f74526bd5a7cf4e",
- "3fd5354a6190903d6a0b661fe177daf6", "EA586FE5C31B38A547D18332141665E9",
+ "3fd5354a6190903d6a0b661fe177daf6", "02BB1B12EC2CD3D9E08706D177309B25",
];
private static string[] Digests16x16 = [
@@ -96,7 +96,7 @@ public class Av1PredictorTests
"bc05c46f18d0638f0228f1de64f07cd5", "204e613e429935f721a5b29cec7d44bb",
"aa0a7c9a7482dfc06d9685072fc5bafd", "ffb60f090d83c624bb4f7dc3a630ac4f",
"36bcb9ca9bb5eac520b050409de25da5", "34d9a5dd3363668391bc3bd05b468182",
- "1e149c28db8b234e43931c347a523794", "DD9FCEB000F2B2F1F4910AE9856BCF4C",
+ "1e149c28db8b234e43931c347a523794", "1C0AF3C1B39A4C1866440D4C80A8CE8E",
];
private static string[] Digests16x64 = [
@@ -104,7 +104,7 @@ public class Av1PredictorTests
"12b0c69595328c465e0b25e0c9e3e9fc", "3b2a053ee8b05a8ac35ad23b0422a151",
"f3be77c0fe67eb5d9d515e92bec21eb7", "f1ece6409e01e9dd98b800d49628247d",
"efd2ec9bfbbd4fd1f6604ea369df1894", "ec703de918422b9e03197ba0ed60a199",
- "739418efb89c07f700895deaa5d0b3e3", "AE9A0FBC3EB929B82E20E8C45001E7BE",
+ "739418efb89c07f700895deaa5d0b3e3", "272C2EAFEABCDA41F27EFBB48B938211",
];
private static string[] Digests32x8 = [
@@ -112,7 +112,7 @@ public class Av1PredictorTests
"79b799f1eb77d5189535dc4e18873a0e", "90e943adf3de4f913864dce4e52b4894",
"5e1b9cc800a89ef45f5bdcc9e99e4e96", "3103405df20d254cbf32ac30872ead4b",
"648550e369b77687bff3c7d6f249b02f", "f9f73bcd8aadfc059fa260325df957a1",
- "204cef70d741c25d4fe2b1d10d2649a5", "8CB3C74FFFF9975F3DE5178311486350",
+ "204cef70d741c25d4fe2b1d10d2649a5", "A71D39B79759B44AF95E3C03D2D72C56",
];
private static string[] Digests32x16 = [
@@ -120,7 +120,7 @@ public class Av1PredictorTests
"6ad3bb37ebe8374b0a4c2d18fe3ebb6a", "08d3cfe7a1148bff55eb6166da3378c6",
"656a722394764d17b6c42401b9e0ad3b", "4aa00c192102efeb325883737e562f0d",
"9881a90ca88bca4297073e60b3bb771a", "8cd74aada398a3d770fc3ace38ecd311",
- "0a927e3f5ff8e8338984172cc0653b13", "91ABCB84EB86746DEF31AF8F96BAA0CF",
+ "0a927e3f5ff8e8338984172cc0653b13", "B48D4391B5637D085F128C7137739400",
];
private static string[] Digests32x32 = [
@@ -136,7 +136,7 @@ public class Av1PredictorTests
"4e2a2cfd8f56f15939bdfc753145b303", "0ce332b343934b34cd4417725faa85cb",
"1d2f8e48e3adb7c448be05d9f66f4954", "9fb2e176636a5689b26f73ca73fcc512",
"e720ebccae7e25e36f23da53ae5b5d6a", "86fe4364734169aaa4520d799890d530",
- "b1870290764bb1b100d1974e2bd70f1d", "B652BBE65C345A7153A1F3E1C801633F",
+ "b1870290764bb1b100d1974e2bd70f1d", "2579B3CD9B1252A282C79C36B47FA186",
];
private static string[] Digests64x16 = [
@@ -144,7 +144,7 @@ public class Av1PredictorTests
"69e462c3338a9aaf993c3f7cfbc15649", "821b76b1494d4f84d20817840f719a1a",
"fd9b4276e7affe1e0e4ce4f428058994", "cd82fd361a4767ac29a9f406b480b8f3",
"2792c2f810157a4a6cb13c28529ff779", "1220442d90c4255ba0969d28b91e93a6",
- "c7253e10b45f7f67dfee3256c9b94825", "F0DC6EE8E9291452AC361E08AEB53DB5",
+ "c7253e10b45f7f67dfee3256c9b94825", "103DA3694F8D5C6F79CA854418F3E3F9",
];
private static string[] Digests64x32 = [
@@ -152,7 +152,7 @@ public class Av1PredictorTests
"538424b24bd0830f21788e7238ca762f", "a6c5aeb722615089efbca80b02951ceb",
"12604b37875533665078405ef4582e35", "0048afa17bd3e1632d68b96048836530",
"07a0cfcb56a5eed50c4bd6c26814336b", "529d8a070de5bc6531fa3ee8f450c233",
- "33c50a11c7d78f72434064f634305e95", "28E560D9C16C5ED6055A66AB16EC6900",
+ "33c50a11c7d78f72434064f634305e95", "98BEB315BF0A9B734D4FB137FDE00DF4",
];
private static string[] Digests64x64 = [