diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md
index 6aee22365f..99052c0d0c 100644
--- a/HEIF_IMPLEMENTATION_PLAN.md
+++ b/HEIF_IMPLEMENTATION_PLAN.md
@@ -338,10 +338,29 @@ Independent loop-filter delta entropy correction, verified on 2026-09-05:
`av1/encoder/encodeframe.c:2357`). Existing native output must not be assumed to exercise multi-delta syntax.
A complete independently authored multi-delta bitstream remains a verification gap. This correction does not
establish encoder parity or complete decoder correctness. No benchmark was run.
-- Following the decoded values into deblocking found a further clipping-order discrepancy:
- `Av1LoopFilterDecoder.cs:373-390` clips the reference adjustment before adding the mode adjustment;
- native `av1/common/av1_loopfilter.c:95-101,195-201` clips their combined result once. A production-frame
- regression and correction are still pending for that separately identified numerical defect.
+
+Loop-filter level clipping correction after checkpoint `77f535828`, verified on 2026-09-05:
+
+- Following decoded delta values into deblocking found another numerical defect. At the preceding checkpoint,
+ `Av1LoopFilterDecoder.cs:373-390` clipped the reference adjustment before adding the mode adjustment.
+ Native `av1/common/av1_loopfilter.c:95-101,182-187` clips their combined result once, both for per-block
+ delta-LF and for the precomputed frame-level table. Opposite adjustments must be allowed to cancel before clipping.
+- The production-frame regression failed before correction: base level 1, reference delta -63, and mode delta +63
+ should retain level 1, but premature clipping produced level 63. An expected sample of 100 became 104 at the first
+ differing position (`loop-level-before.trx`); later edge samples also differed. VSTest stopped on that first failure.
+ The local runner then failed to print xUnit's Unicode arrows under cp1252. The saved report was inspected and the
+ runner's stdout encoding corrected; the failed test was not rerun before the implementation change.
+- `Av1LoopFilterDecoder.GetFilterLevel` now adds both adjustments using the scale derived from the original level,
+ then clips once. Existing base/delta-LF and segmentation clipping remain in their normative order.
+ No allocation, ownership, or syntax policy changed.
+- The existing production-frame test and its three expected outputs are retained. Four new cases exercise cancelling
+ deltas at both limits, with delta-LF present and absent, through `DecodeFrame` and an independent scalar filter.
+ Final Release net11.0 incremental build reported zero errors and zero warnings; the earlier test compilation
+ reported 1,009 existing warnings. Roslynk reports zero compiler errors.
+- Serialized Visual Studio VSTest passed **11/11** in `loop-level-final.trx` (5.7519 seconds): deblocking scalar/SIMD
+ definitions, production-frame delta cases, and native deblocking, CDF-update, profile, and all-intra fixtures.
+ The earlier 2,095-case report applies to the preceding checkpoint. No benchmark or separate-encoder parity
+ measurement was run, and the complete multi-delta bitstream verification gap remains open.
Motion-controller investigation continued after correction checkpoint `578ec34d9`:
diff --git a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs
index 8dbc55869b..47e7208e5d 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Pipeline/LoopFilter/Av1LoopFilterDecoder.cs
@@ -372,10 +372,7 @@ internal sealed class Av1LoopFilterDecoder
{
int referenceScale = 1 << (level >> 5);
Av1ReferenceFrameType referenceFrame = modeInfo.ReferenceFrames[0];
- level = Av1Math.Clip3(
- 0,
- Av1Constants.MaxLoopFilter,
- level + (parameters.ReferenceDeltas[(int)referenceFrame] * referenceScale));
+ level += parameters.ReferenceDeltas[(int)referenceFrame] * referenceScale;
if (referenceFrame > Av1ReferenceFrameType.Intra)
{
@@ -384,11 +381,12 @@ internal sealed class Av1LoopFilterDecoder
int modeDeltaIndex = modeInfo.YMode is Av1PredictionMode.GlobalMotionVector or
Av1PredictionMode.GlobalGlobalMotionVector ? 0 : 1;
- level = Av1Math.Clip3(
- 0,
- Av1Constants.MaxLoopFilter,
- level + (parameters.ModeDeltas[modeDeltaIndex] * referenceScale));
+ level += parameters.ModeDeltas[modeDeltaIndex] * referenceScale;
}
+
+ // Reference and mode adjustments use the same scale and may cancel beyond either limit.
+ // Clipping the intermediate reference sum would discard part of that cancellation.
+ level = Av1Math.Clip3(0, Av1Constants.MaxLoopFilter, level);
}
return level;
diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1DeblockingFilterTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1DeblockingFilterTests.cs
index 24080444e4..df8237eb2c 100644
--- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1DeblockingFilterTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1DeblockingFilterTests.cs
@@ -59,19 +59,53 @@ public class Av1DeblockingFilterTests
ValidateInterEdgeAndDeltaDecisions(
Av1PredictionMode.GlobalMotionVector,
Av1ReferenceFrameType.Last,
+ 20,
+ -3,
+ 4,
+ false,
17);
ValidateInterEdgeAndDeltaDecisions(
Av1PredictionMode.NewMotionVector,
Av1ReferenceFrameType.Last,
+ 20,
+ -3,
+ 4,
+ false,
21);
ValidateInterEdgeAndDeltaDecisions(
Av1PredictionMode.GlobalMotionVector,
Av1ReferenceFrameType.Golden,
+ 20,
+ 2,
+ 4,
+ false,
22);
}
+ ///
+ /// Verifies that opposite reference and mode adjustments cancel before the final filter-level clamp.
+ ///
+ [Theory]
+ [InlineData(1, -63, 63, false)]
+ [InlineData(63, 63, -63, false)]
+ [InlineData(1, -63, 63, true)]
+ [InlineData(63, 63, -63, true)]
+ public void DecodeFrameCombinesDeltasBeforeClipping(int baseLevel, int referenceDelta, int modeDelta, bool deltaLoopFilterPresent)
+ {
+ // Both native paths, per-block delta-LF and the precomputed frame table, clamp only after adding
+ // reference and mode adjustments. These equal and opposite deltas leave the base level unchanged.
+ ValidateInterEdgeAndDeltaDecisions(
+ Av1PredictionMode.NewMotionVector,
+ Av1ReferenceFrameType.Last,
+ baseLevel,
+ referenceDelta,
+ modeDelta,
+ deltaLoopFilterPresent,
+ baseLevel);
+ }
+
///
/// Exercises mixed flatness, high-edge-variance, disabled-mask, direction, and bit-depth cases.
///
@@ -109,12 +143,15 @@ public class Av1DeblockingFilterTests
private static void ValidateInterEdgeAndDeltaDecisions(
Av1PredictionMode mode,
Av1ReferenceFrameType referenceFrame,
+ int baseLevel,
+ int referenceDelta,
+ int modeDelta,
+ bool deltaLoopFilterPresent,
int expectedLevel)
{
const int width = Stride;
const int height = 8;
const int edge = 16;
- const int baseLevel = 20;
ObuSequenceHeader sequenceHeader = new()
{
MaxFrameWidth = width,
@@ -140,11 +177,12 @@ public class Av1DeblockingFilterTests
};
ObuLoopFilterParameters filterParameters = frameHeader.LoopFilterParameters;
+ frameHeader.DeltaQParameters.IsPresent = deltaLoopFilterPresent;
+ frameHeader.DeltaLoopFilterParameters.IsPresent = deltaLoopFilterPresent;
filterParameters.FilterLevel[0] = baseLevel;
filterParameters.ReferenceDeltaModeEnabled = true;
- filterParameters.ReferenceDeltas[(int)Av1ReferenceFrameType.Last] = -3;
- filterParameters.ReferenceDeltas[(int)Av1ReferenceFrameType.Golden] = 2;
- filterParameters.ModeDeltas[1] = 4;
+ filterParameters.ReferenceDeltas[(int)referenceFrame] = referenceDelta;
+ filterParameters.ModeDeltas[1] = modeDelta;
using Av1FrameBuffer frameBuffer = new(
Configuration.Default,