diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md index 673384cc6..79330e2cc 100644 --- a/HEIF_IMPLEMENTATION_PLAN.md +++ b/HEIF_IMPLEMENTATION_PLAN.md @@ -30,8 +30,8 @@ Reference checkout evidence on 2026-08-31: Reconciled with the worktree on 2026-08-31. - [~] The bounded container reader, still-image path, sequence parser, AV1 decoder, color pipeline, presentation pipeline, and broad AV1 test suite exist locally. -- [~] The inter-frame decoder has verified checkpoints through scaled-reference prediction. Local warped - motion and global motion exist locally but remain open until their ordered checkpoints below are completed. +- [~] The inter-frame decoder has verified checkpoints through local warped prediction. Global motion exists + locally but remains open until its ordered checkpoint below is completed. - [~] Loop filtering, CDEF, super-resolution, restoration, film grain, layered presentation, alpha composition, and color conversion exist locally. Shared-source cleanup changed the current tree, so final production-path verification is open. - [~] AV1 writer primitives, forward transforms, symbol encoding, and tile-writing source exist locally, but they are not connected to the public encoder. - [ ] The public AV1 encoder is not implemented. HeifEncoderCore.Encode throws NotSupportedException when AV1 is selected. @@ -184,8 +184,8 @@ The single-reference syntax, buffer, reconstruction, and ownership foundation is - [x] Difference-weighted compound prediction. - [x] OBMC. - [x] Scaled-reference prediction. -- [~] Local warped prediction. Next item. -- [~] Non-translational global prediction. +- [x] Local warped prediction. +- [~] Non-translational global prediction. Next item. - [~] Inter deblocking decisions and reference/mode deltas. Verified equal-average compound checkpoint evidence on 2026-08-31: @@ -429,6 +429,42 @@ Verified scaled-reference checkpoint evidence on 2026-08-31: - [x] The focused Release checkpoint set passes 10/10 on net10.0 and 10/10 on net11.0, with zero failures or skips. Scoped analyzer and whitespace verification pass for every changed C# file. Roslynk reports zero compiler errors, `git diff --check` passes, and `.gitattributes` is unchanged. +- [x] The completed checkpoint was committed as `658a9cd1b6e22806decbae923da8800bca03a09e` + with author and committer `James Jackson-South `. + +Verified local warped-prediction checkpoint evidence on 2026-08-31: + +- [x] Refreshed the clean official libaom `main` checkout and audited the observed revision + `441c439b9916474cac15d2822af47a9ad70674a8`. Motion-mode eligibility and CDF selection match + `read_motion_mode` in `av1/decoder/decodemv.c`; above, left, top-left, and top-right spatial projection + samples and threshold selection match `findSamples` and `selectSamples` in + `av1/common/mvref_common.c`; affine fitting, shear reduction, phase derivation, filters, rounding, + clipping, and invalid-model fallback match `av1/common/warped_motion.c` and + `av1/common/reconinter.c`. +- [x] Mechanically compared all 1,544 ImageSharp and independent-test warped-filter coefficients against + current libaom's `av1_warped_filter`; both comparisons have zero differences. The separate scalar test + transcription covers 8-, 10-, and 12-bit luma and subsampled-chroma coordinates, tail widths, destination + stride preservation, libaom's 12-bit round adjustment, and AVX-512, AVX, 128-bit, and scalar dispatch + through `FeatureTestRunner`. +- [x] Extracted the fixture's exact 2,310-byte AV1 `mdat` payload at AVIF offset 997. Its SHA-256 is + `644D04FE1D1A32BB7A3856AD7EB49CF1EFDE0AC845E55BEAC4170F72353F2391`. Current official + libaom decoded both 256x256 YUV444 frames with one thread, row threading disabled, and all layers enabled. + The complete Y4M SHA-256 is + `8FDC5D46014F5E5A7455A83643AB6F0DA66FC5A984E72A43F8C75BAD8271C299`; the final + frame's 196,608 native samples have SHA-256 + `47B2AB39BF3B9DA15C1EC59840F964DFDF227760947F6E1295FB38A84555F75C` and match the + retained native reference with zero differences. +- [x] The real two-frame fixture exercises `Av1BlockDecoder.DecodeBlock()`, requires decoded + `WARPED_CAUSAL` state and the expected multi-sample affine model, compares final native Y, U, and V + planes exactly, compares the retained final presentation through ImageSharp's established reference-output + API, and passes through intrinsic and scalar dispatch. The 1,024-byte constrained tracked-allocator path + passes with motion-field allocations present and balanced exactly-once returns. +- [x] Renamed the stale pinned-reference test and its contract-derived PNG together without changing the PNG + bytes. Its SHA-256 remains + `4490D62FB6679378E92CACA48427359091AD2106BE49FC1A3848F78BE03BEEB1`. +- [x] The focused Release checkpoint set passes 4/4 on net10.0 and 4/4 on net11.0, with zero failures or + skips. Scoped analyzer verification passes for both changed C# files. Roslynk reports zero compiler errors, + `git diff --check` passes, and `.gitattributes` is unchanged. For every item: diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs index 689346079..e7d9ac9dd 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReconstructionConformanceTests.cs @@ -1079,11 +1079,11 @@ public class Av1ReconstructionConformanceTests AverageCompoundFixtureFrameCount); /// - /// Verifies production local warped-motion reconstruction against pinned native and presentation references. + /// Verifies production local warped-motion reconstruction against current-libaom native and retained presentation references. /// [Theory] [WithFile(TestImages.Heif.Av1LocalWarpSequenceAvif, PixelTypes.Rgba32)] - public void DecodeRealLibavifLocalWarpSequenceMatchesPinnedReferences(TestImageProvider provider) + public void DecodeRealLibavifLocalWarpSequenceMatchesCurrentLibaomReferences(TestImageProvider provider) => FeatureTestRunner.RunWithHwIntrinsicsFeature( ValidateLocalWarpSequenceWithDefaultConfiguration, ReconstructionConfigurations, diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1WarpedInterPredictorTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1WarpedInterPredictorTests.cs index b7507578b..5ebe40bac 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1WarpedInterPredictorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1WarpedInterPredictorTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. +using System.Numerics; using SixLabors.ImageSharp.Formats.Heif.Av1.Motion; using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.Inter; using SixLabors.ImageSharp.Tests.TestUtilities; @@ -14,26 +15,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; public class Av1WarpedInterPredictorTests { /// - /// The hardware configurations covering the portable vector operator and scalar fallback. + /// The hardware configurations covering every descending SIMD width and the scalar fallback. /// - private const HwIntrinsics PredictorConfigurations = HwIntrinsics.AllowAll | HwIntrinsics.DisableHWIntrinsic; + private const HwIntrinsics PredictorConfigurations = + HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic; /// - /// Verifies exact 8-bit vector/scalar parity for luma and subsampled chroma coordinates. + /// Verifies exact 8-bit prediction against the current libaom scalar equations. /// [Fact] - public void BytePredictionMatchesScalarAcrossIntrinsicConfigurations() + public void BytePredictionMatchesCurrentLibaomOracleAcrossIntrinsicConfigurations() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateBytePrediction, PredictorConfigurations); /// - /// Verifies exact 8-, 10-, and 12-bit vector/scalar parity for luma and subsampled chroma coordinates. + /// Verifies exact 8-, 10-, and 12-bit prediction against the current libaom scalar equations. /// [Fact] - public void HighBitDepthPredictionMatchesScalarAcrossIntrinsicConfigurations() + public void HighBitDepthPredictionMatchesCurrentLibaomOracleAcrossIntrinsicConfigurations() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateHighBitDepthPrediction, PredictorConfigurations); /// - /// Applies the pinned multi-sample affine model to deterministic byte storage. + /// Applies the current-libaom multi-sample affine model to deterministic byte storage. /// private static void ValidateBytePrediction() { @@ -52,18 +54,17 @@ public class Av1WarpedInterPredictorTests } } - Av1GlobalMotionParameters parameters = CreatePinnedParameters(); + Av1GlobalMotionParameters parameters = CreateCurrentLibaomParameters(); for (int subsampling = 0; subsampling <= 1; subsampling++) { byte[] expected = new byte[destinationStride * height]; byte[] actual = new byte[destinationStride * height]; Array.Fill(expected, (byte)0xD3); Array.Fill(actual, (byte)0xD3); - short[] expectedScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength]; short[] actualScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength]; Point destinationPosition = subsampling == 0 ? new Point(32, 24) : new Point(16, 12); - Av1WarpedInterPredictor.PredictWarpedScalar( + PredictCurrentLibaomReference( source, sourceStride, new Point(padding, padding), @@ -76,8 +77,8 @@ public class Av1WarpedInterPredictorTests height, subsampling, subsampling, - parameters, - expectedScratch); + 8, + parameters); Av1WarpedInterPredictor.PredictWarped( source, @@ -100,7 +101,7 @@ public class Av1WarpedInterPredictorTests } /// - /// Applies the pinned multi-sample affine model to every supported high-bit-depth precision. + /// Applies the current-libaom multi-sample affine model to every supported high-bit-depth precision. /// private static void ValidateHighBitDepthPrediction() { @@ -110,7 +111,7 @@ public class Av1WarpedInterPredictorTests const int width = 13; const int height = 11; const int destinationStride = width + 7; - Av1GlobalMotionParameters parameters = CreatePinnedParameters(); + Av1GlobalMotionParameters parameters = CreateCurrentLibaomParameters(); foreach (int bitDepth in new[] { 8, 10, 12 }) { int maximum = (1 << bitDepth) - 1; @@ -121,7 +122,6 @@ public class Av1WarpedInterPredictorTests { source[(row * sourceStride) + column] = (ushort)(((row * 269) + (column * 443) + (row * column * 31)) & maximum); - } } @@ -131,11 +131,10 @@ public class Av1WarpedInterPredictorTests ushort[] actual = new ushort[destinationStride * height]; Array.Fill(expected, (ushort)0xDEAD); Array.Fill(actual, (ushort)0xDEAD); - short[] expectedScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength]; short[] actualScratch = new short[Av1WarpedInterPredictor.WarpedScratchLength]; Point destinationPosition = subsampling == 0 ? new Point(32, 24) : new Point(16, 12); - Av1WarpedInterPredictor.PredictWarpedScalar( + PredictCurrentLibaomReference( source, sourceStride, new Point(padding, padding), @@ -149,8 +148,7 @@ public class Av1WarpedInterPredictorTests subsampling, subsampling, bitDepth, - parameters, - expectedScratch); + parameters); Av1WarpedInterPredictor.PredictWarped( source, @@ -175,9 +173,332 @@ public class Av1WarpedInterPredictorTests } /// - /// Creates one nontrivial affine model traced from the pinned two-frame local-warp fixture. + /// Reconstructs one warped block by directly transcribing current libaom's scalar affine loops. + /// + private static void PredictCurrentLibaomReference( + ReadOnlySpan source, + int sourceStride, + Point sourceOrigin, + int sourceWidth, + int sourceHeight, + Span destination, + int destinationStride, + Point destinationPosition, + int width, + int height, + int subsamplingX, + int subsamplingY, + int bitDepth, + Av1GlobalMotionParameters parameters) + where TPixel : unmanaged, IBinaryInteger + { + const int filterBits = 7; + const int filterTaps = 8; + const int tileSize = 8; + const int modelPrecisionBits = 16; + const int phasePrecisionBits = 10; + const int pixelPrecisionShifts = 64; + Span intermediate = stackalloc int[15 * tileSize]; + + // Current libaom raises round0 by two for 12-bit sources so the biased horizontal intermediate + // remains representable in 16 bits, then removes those two bits from the vertical rounding. + int intermediateRange = bitDepth + filterBits - 3 + 2; + int round0 = 3 + Math.Max(intermediateRange - 16, 0); + int verticalRound = (2 * filterBits) - round0; + int horizontalBias = 1 << (bitDepth + filterBits - 1); + int verticalBias = 1 << (bitDepth + (2 * filterBits) - round0); + int maximum = (1 << bitDepth) - 1; + + for (int tileRow = destinationPosition.Y; tileRow < destinationPosition.Y + height; tileRow += tileSize) + { + for (int tileColumn = destinationPosition.X; tileColumn < destinationPosition.X + width; tileColumn += tileSize) + { + int centerX = (tileColumn + 4) << subsamplingX; + int centerY = (tileRow + 4) << subsamplingY; + long projectedX = + ((long)parameters[2] * centerX) + ((long)parameters[3] * centerY) + parameters[0]; + + long projectedY = + ((long)parameters[4] * centerX) + ((long)parameters[5] * centerY) + parameters[1]; + + long planeX = projectedX >> subsamplingX; + long planeY = projectedY >> subsamplingY; + int integerX = (int)(planeX >> modelPrecisionBits); + int integerY = (int)(planeY >> modelPrecisionBits); + int phaseX = (int)planeX & ((1 << modelPrecisionBits) - 1); + int phaseY = (int)planeY & ((1 << modelPrecisionBits) - 1); + phaseX += (-4 * parameters.Alpha) + (-4 * parameters.Beta); + phaseY += (-4 * parameters.Gamma) + (-4 * parameters.Delta); + phaseX &= -1 << 6; + phaseY &= -1 << 6; + + for (int row = -7; row < 8; row++) + { + int sourceY = Math.Clamp(integerY + row, 0, sourceHeight - 1); + int phase = phaseX + (parameters.Beta * (row + 4)); + for (int column = -4; column < 4; column++) + { + int sourceX = integerX + column - 3; + int filterIndex = RoundPowerOfTwo(phase, phasePrecisionBits) + pixelPrecisionShifts; + int coefficientOffset = filterIndex * filterTaps; + int sum = horizontalBias; + for (int tap = 0; tap < filterTaps; tap++) + { + int sampleX = Math.Clamp(sourceX + tap, 0, sourceWidth - 1); + int sourceIndex = + ((sourceOrigin.Y + sourceY) * sourceStride) + sourceOrigin.X + sampleX; + + int sample = int.CreateChecked(source[sourceIndex]); + sum += sample * CurrentLibaomWarpedFilter[coefficientOffset + tap]; + } + + intermediate[((row + 7) * tileSize) + column + 4] = RoundPowerOfTwo(sum, round0); + phase += parameters.Alpha; + } + } + + int lastRow = Math.Min(4, destinationPosition.Y + height - tileRow - 4); + for (int row = -4; row < lastRow; row++) + { + int phase = phaseY + (parameters.Delta * (row + 4)); + int lastColumn = Math.Min(4, destinationPosition.X + width - tileColumn - 4); + for (int column = -4; column < lastColumn; column++) + { + int filterIndex = RoundPowerOfTwo(phase, phasePrecisionBits) + pixelPrecisionShifts; + int coefficientOffset = filterIndex * filterTaps; + int sum = verticalBias; + for (int tap = 0; tap < filterTaps; tap++) + { + int intermediateIndex = ((row + tap + 4) * tileSize) + column + 4; + sum += intermediate[intermediateIndex] * CurrentLibaomWarpedFilter[coefficientOffset + tap]; + } + + int value = RoundPowerOfTwo(sum, verticalRound) - (1 << (bitDepth - 1)) - (1 << bitDepth); + int destinationIndex = + ((tileRow - destinationPosition.Y + row + 4) * destinationStride) + + tileColumn - + destinationPosition.X + + column + + 4; + + destination[destinationIndex] = TPixel.CreateChecked(Math.Clamp(value, 0, maximum)); + phase += parameters.Gamma; + } + } + } + } + } + + /// + /// Gets the 193 current-libaom eight-tap warped-filter phases used by the independent scalar oracle. + /// + private static ReadOnlySpan CurrentLibaomWarpedFilter => + [ + 0, 0, 127, 1, 0, 0, 0, 0, + 0, -1, 127, 2, 0, 0, 0, 0, + 1, -3, 127, 4, -1, 0, 0, 0, + 1, -4, 126, 6, -2, 1, 0, 0, + 1, -5, 126, 8, -3, 1, 0, 0, + 1, -6, 125, 11, -4, 1, 0, 0, + 1, -7, 124, 13, -4, 1, 0, 0, + 2, -8, 123, 15, -5, 1, 0, 0, + 2, -9, 122, 18, -6, 1, 0, 0, + 2, -10, 121, 20, -6, 1, 0, 0, + 2, -11, 120, 22, -7, 2, 0, 0, + 2, -12, 119, 25, -8, 2, 0, 0, + 3, -13, 117, 27, -8, 2, 0, 0, + 3, -13, 116, 29, -9, 2, 0, 0, + 3, -14, 114, 32, -10, 3, 0, 0, + 3, -15, 113, 35, -10, 2, 0, 0, + 3, -15, 111, 37, -11, 3, 0, 0, + 3, -16, 109, 40, -11, 3, 0, 0, + 3, -16, 108, 42, -12, 3, 0, 0, + 4, -17, 106, 45, -13, 3, 0, 0, + 4, -17, 104, 47, -13, 3, 0, 0, + 4, -17, 102, 50, -14, 3, 0, 0, + 4, -17, 100, 52, -14, 3, 0, 0, + 4, -18, 98, 55, -15, 4, 0, 0, + 4, -18, 96, 58, -15, 3, 0, 0, + 4, -18, 94, 60, -16, 4, 0, 0, + 4, -18, 91, 63, -16, 4, 0, 0, + 4, -18, 89, 65, -16, 4, 0, 0, + 4, -18, 87, 68, -17, 4, 0, 0, + 4, -18, 85, 70, -17, 4, 0, 0, + 4, -18, 82, 73, -17, 4, 0, 0, + 4, -18, 80, 75, -17, 4, 0, 0, + 4, -18, 78, 78, -18, 4, 0, 0, + 4, -17, 75, 80, -18, 4, 0, 0, + 4, -17, 73, 82, -18, 4, 0, 0, + 4, -17, 70, 85, -18, 4, 0, 0, + 4, -17, 68, 87, -18, 4, 0, 0, + 4, -16, 65, 89, -18, 4, 0, 0, + 4, -16, 63, 91, -18, 4, 0, 0, + 4, -16, 60, 94, -18, 4, 0, 0, + 3, -15, 58, 96, -18, 4, 0, 0, + 4, -15, 55, 98, -18, 4, 0, 0, + 3, -14, 52, 100, -17, 4, 0, 0, + 3, -14, 50, 102, -17, 4, 0, 0, + 3, -13, 47, 104, -17, 4, 0, 0, + 3, -13, 45, 106, -17, 4, 0, 0, + 3, -12, 42, 108, -16, 3, 0, 0, + 3, -11, 40, 109, -16, 3, 0, 0, + 3, -11, 37, 111, -15, 3, 0, 0, + 2, -10, 35, 113, -15, 3, 0, 0, + 3, -10, 32, 114, -14, 3, 0, 0, + 2, -9, 29, 116, -13, 3, 0, 0, + 2, -8, 27, 117, -13, 3, 0, 0, + 2, -8, 25, 119, -12, 2, 0, 0, + 2, -7, 22, 120, -11, 2, 0, 0, + 1, -6, 20, 121, -10, 2, 0, 0, + 1, -6, 18, 122, -9, 2, 0, 0, + 1, -5, 15, 123, -8, 2, 0, 0, + 1, -4, 13, 124, -7, 1, 0, 0, + 1, -4, 11, 125, -6, 1, 0, 0, + 1, -3, 8, 126, -5, 1, 0, 0, + 1, -2, 6, 126, -4, 1, 0, 0, + 0, -1, 4, 127, -3, 1, 0, 0, + 0, 0, 2, 127, -1, 0, 0, 0, + 0, 0, 0, 127, 1, 0, 0, 0, + 0, 0, -1, 127, 2, 0, 0, 0, + 0, 1, -3, 127, 4, -2, 1, 0, + 0, 1, -5, 127, 6, -2, 1, 0, + 0, 2, -6, 126, 8, -3, 1, 0, + -1, 2, -7, 126, 11, -4, 2, -1, + -1, 3, -8, 125, 13, -5, 2, -1, + -1, 3, -10, 124, 16, -6, 3, -1, + -1, 4, -11, 123, 18, -7, 3, -1, + -1, 4, -12, 122, 20, -7, 3, -1, + -1, 4, -13, 121, 23, -8, 3, -1, + -2, 5, -14, 120, 25, -9, 4, -1, + -1, 5, -15, 119, 27, -10, 4, -1, + -1, 5, -16, 118, 30, -11, 4, -1, + -2, 6, -17, 116, 33, -12, 5, -1, + -2, 6, -17, 114, 35, -12, 5, -1, + -2, 6, -18, 113, 38, -13, 5, -1, + -2, 7, -19, 111, 41, -14, 6, -2, + -2, 7, -19, 110, 43, -15, 6, -2, + -2, 7, -20, 108, 46, -15, 6, -2, + -2, 7, -20, 106, 49, -16, 6, -2, + -2, 7, -21, 104, 51, -16, 7, -2, + -2, 7, -21, 102, 54, -17, 7, -2, + -2, 8, -21, 100, 56, -18, 7, -2, + -2, 8, -22, 98, 59, -18, 7, -2, + -2, 8, -22, 96, 62, -19, 7, -2, + -2, 8, -22, 94, 64, -19, 7, -2, + -2, 8, -22, 91, 67, -20, 8, -2, + -2, 8, -22, 89, 69, -20, 8, -2, + -2, 8, -22, 87, 72, -21, 8, -2, + -2, 8, -21, 84, 74, -21, 8, -2, + -2, 8, -22, 82, 77, -21, 8, -2, + -2, 8, -21, 79, 79, -21, 8, -2, + -2, 8, -21, 77, 82, -22, 8, -2, + -2, 8, -21, 74, 84, -21, 8, -2, + -2, 8, -21, 72, 87, -22, 8, -2, + -2, 8, -20, 69, 89, -22, 8, -2, + -2, 8, -20, 67, 91, -22, 8, -2, + -2, 7, -19, 64, 94, -22, 8, -2, + -2, 7, -19, 62, 96, -22, 8, -2, + -2, 7, -18, 59, 98, -22, 8, -2, + -2, 7, -18, 56, 100, -21, 8, -2, + -2, 7, -17, 54, 102, -21, 7, -2, + -2, 7, -16, 51, 104, -21, 7, -2, + -2, 6, -16, 49, 106, -20, 7, -2, + -2, 6, -15, 46, 108, -20, 7, -2, + -2, 6, -15, 43, 110, -19, 7, -2, + -2, 6, -14, 41, 111, -19, 7, -2, + -1, 5, -13, 38, 113, -18, 6, -2, + -1, 5, -12, 35, 114, -17, 6, -2, + -1, 5, -12, 33, 116, -17, 6, -2, + -1, 4, -11, 30, 118, -16, 5, -1, + -1, 4, -10, 27, 119, -15, 5, -1, + -1, 4, -9, 25, 120, -14, 5, -2, + -1, 3, -8, 23, 121, -13, 4, -1, + -1, 3, -7, 20, 122, -12, 4, -1, + -1, 3, -7, 18, 123, -11, 4, -1, + -1, 3, -6, 16, 124, -10, 3, -1, + -1, 2, -5, 13, 125, -8, 3, -1, + -1, 2, -4, 11, 126, -7, 2, -1, + 0, 1, -3, 8, 126, -6, 2, 0, + 0, 1, -2, 6, 127, -5, 1, 0, + 0, 1, -2, 4, 127, -3, 1, 0, + 0, 0, 0, 2, 127, -1, 0, 0, + 0, 0, 0, 1, 127, 0, 0, 0, + 0, 0, 0, -1, 127, 2, 0, 0, + 0, 0, 1, -3, 127, 4, -1, 0, + 0, 0, 1, -4, 126, 6, -2, 1, + 0, 0, 1, -5, 126, 8, -3, 1, + 0, 0, 1, -6, 125, 11, -4, 1, + 0, 0, 1, -7, 124, 13, -4, 1, + 0, 0, 2, -8, 123, 15, -5, 1, + 0, 0, 2, -9, 122, 18, -6, 1, + 0, 0, 2, -10, 121, 20, -6, 1, + 0, 0, 2, -11, 120, 22, -7, 2, + 0, 0, 2, -12, 119, 25, -8, 2, + 0, 0, 3, -13, 117, 27, -8, 2, + 0, 0, 3, -13, 116, 29, -9, 2, + 0, 0, 3, -14, 114, 32, -10, 3, + 0, 0, 3, -15, 113, 35, -10, 2, + 0, 0, 3, -15, 111, 37, -11, 3, + 0, 0, 3, -16, 109, 40, -11, 3, + 0, 0, 3, -16, 108, 42, -12, 3, + 0, 0, 4, -17, 106, 45, -13, 3, + 0, 0, 4, -17, 104, 47, -13, 3, + 0, 0, 4, -17, 102, 50, -14, 3, + 0, 0, 4, -17, 100, 52, -14, 3, + 0, 0, 4, -18, 98, 55, -15, 4, + 0, 0, 4, -18, 96, 58, -15, 3, + 0, 0, 4, -18, 94, 60, -16, 4, + 0, 0, 4, -18, 91, 63, -16, 4, + 0, 0, 4, -18, 89, 65, -16, 4, + 0, 0, 4, -18, 87, 68, -17, 4, + 0, 0, 4, -18, 85, 70, -17, 4, + 0, 0, 4, -18, 82, 73, -17, 4, + 0, 0, 4, -18, 80, 75, -17, 4, + 0, 0, 4, -18, 78, 78, -18, 4, + 0, 0, 4, -17, 75, 80, -18, 4, + 0, 0, 4, -17, 73, 82, -18, 4, + 0, 0, 4, -17, 70, 85, -18, 4, + 0, 0, 4, -17, 68, 87, -18, 4, + 0, 0, 4, -16, 65, 89, -18, 4, + 0, 0, 4, -16, 63, 91, -18, 4, + 0, 0, 4, -16, 60, 94, -18, 4, + 0, 0, 3, -15, 58, 96, -18, 4, + 0, 0, 4, -15, 55, 98, -18, 4, + 0, 0, 3, -14, 52, 100, -17, 4, + 0, 0, 3, -14, 50, 102, -17, 4, + 0, 0, 3, -13, 47, 104, -17, 4, + 0, 0, 3, -13, 45, 106, -17, 4, + 0, 0, 3, -12, 42, 108, -16, 3, + 0, 0, 3, -11, 40, 109, -16, 3, + 0, 0, 3, -11, 37, 111, -15, 3, + 0, 0, 2, -10, 35, 113, -15, 3, + 0, 0, 3, -10, 32, 114, -14, 3, + 0, 0, 2, -9, 29, 116, -13, 3, + 0, 0, 2, -8, 27, 117, -13, 3, + 0, 0, 2, -8, 25, 119, -12, 2, + 0, 0, 2, -7, 22, 120, -11, 2, + 0, 0, 1, -6, 20, 121, -10, 2, + 0, 0, 1, -6, 18, 122, -9, 2, + 0, 0, 1, -5, 15, 123, -8, 2, + 0, 0, 1, -4, 13, 124, -7, 1, + 0, 0, 1, -4, 11, 125, -6, 1, + 0, 0, 1, -3, 8, 126, -5, 1, + 0, 0, 1, -2, 6, 126, -4, 1, + 0, 0, 0, -1, 4, 127, -3, 1, + 0, 0, 0, 0, 2, 127, -1, 0, + 0, 0, 0, 0, 2, 127, -1, 0, + ]; + + /// + /// Divides a signed fixed-point value by a power of two with current libaom's rounding. + /// + private static int RoundPowerOfTwo(int value, int bitCount) + => (value + (1 << (bitCount - 1))) >> bitCount; + + /// + /// Creates one nontrivial affine model traced from the current-libaom-verified two-frame local-warp fixture. /// - private static Av1GlobalMotionParameters CreatePinnedParameters() + private static Av1GlobalMotionParameters CreateCurrentLibaomParameters() { Av1GlobalMotionParameters parameters = Av1GlobalMotionParameters.Identity; parameters.Type = Av1GlobalMotionType.Affine; diff --git a/tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifLocalWarpSequenceMatchesPinnedReferences_Rgba32_libavif-rotating-grid-local-warp.png b/tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifLocalWarpSequenceMatchesCurrentLibaomReferences_Rgba32_libavif-rotating-grid-local-warp.png similarity index 100% rename from tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifLocalWarpSequenceMatchesPinnedReferences_Rgba32_libavif-rotating-grid-local-warp.png rename to tests/Images/External/ReferenceOutput/Av1ReconstructionConformanceTests/DecodeRealLibavifLocalWarpSequenceMatchesCurrentLibaomReferences_Rgba32_libavif-rotating-grid-local-warp.png diff --git a/tests/Images/Input/Heif/Av1/Conformance/README.md b/tests/Images/Input/Heif/Av1/Conformance/README.md index 464bd8c66..49167989c 100644 --- a/tests/Images/Input/Heif/Av1/Conformance/README.md +++ b/tests/Images/Input/Heif/Av1/Conformance/README.md @@ -229,15 +229,39 @@ bytes. Their SHA-256 values remain ## Local warped-motion fixture -The `libavif-rotating-grid-local-warp.avif` fixture was encoded from a deterministic two-frame 256x256 limited-range YUV444 source. The source combines checkerboard, ring, and chroma-gradient detail; its second frame rotates the first by 2.5 degrees with nearest-neighbor sampling and edge clamping. The two-frame source Y4M SHA-256 is `82C1468C95C996B05165590417184F59373D67896F8C7B0EB398582C29C9C7A7`. Pinned scalar libavif and libaom generated the fixture and references with: +The `libavif-rotating-grid-local-warp.avif` file is retained solely as interoperability input. It was +originally encoded from a deterministic two-frame 256x256 limited-range YUV444 source whose second frame +rotates the first by 2.5 degrees. The original packaging command records fixture provenance only; libavif +is not used as an AV1 implementation or reconstruction reference: ```text ./avifenc -j 1 -s 0 -q 60 -a color:enable-warped-motion=1 -a color:enable-global-motion=0 -a color:enable-obmc=0 rotating-grid-256-two-frame.y4m libavif-rotating-grid-local-warp.avif -./avifdec -j 1 --index 1 libavif-rotating-grid-local-warp.avif libavif-rotating-grid-local-warp-libaom.y4m -./avifdec -j 1 --index 1 libavif-rotating-grid-local-warp.avif libavif-rotating-grid-local-warp-libavif.png ``` -The AVIF SHA-256 is `990BAC4AD443005C217B0DA4FCCFA9ADFB3AA147AD06C85F9A655A4433E9E8A7`. The retained frame-1 Y4M SHA-256 is `984B2815CEE0C05FDE26430F150A21B5C993141E25BA1ED4FDE09372DB64AC13`, and the frame-1 PNG SHA-256 is `4490D62FB6679378E92CACA48427359091AD2106BE49FC1A3848F78BE03BEEB1`. Pinned libaom tracing records many actual `WARPED_CAUSAL` blocks. The multi-sample model at mode-information row 6, column 8 derives matrix `[-191565, 599107, 61755, -140, -6909, 62012]` and reduced shear `[-3776, -128, -7360, -3520]` from four retained neighbor samples. The production test requires decoded warped mode state, compares every final native Y, U, and V sample and the final RGBA presentation exactly, runs normal and scalar dispatch through `FeatureTestRunner`, and repeats reconstruction with constrained tracked allocation. +On 2026-08-31 the clean official libaom `main` checkout was refreshed from its upstream remote. At the +observed revision `441c439b9916474cac15d2822af47a9ad70674a8`, the fixture's 2,310-byte AV1 `mdat` +payload at AVIF offset 997 has SHA-256 +`644D04FE1D1A32BB7A3856AD7EB49CF1EFDE0AC845E55BEAC4170F72353F2391` and was decoded with: + +```text +aomdec --codec=av1 --threads=1 --row-mt=0 --all-layers --output-bit-depth=8 -o local-warp-current-main.y4m local-warp-current-main.obu +``` + +Current libaom produced two 256x256 YUV444 frames. The complete Y4M has SHA-256 +`8FDC5D46014F5E5A7455A83643AB6F0DA66FC5A984E72A43F8C75BAD8271C299`. Its final +frame's 196,608 native samples have SHA-256 +`47B2AB39BF3B9DA15C1EC59840F964DFDF227760947F6E1295FB38A84555F75C` and match the +retained `libavif-rotating-grid-local-warp-libaom.y4m` reference with zero differing samples. + +The multi-sample model at mode-information row 6, column 8 derives matrix +`[-191565, 599107, 61755, -140, -6909, 62012]` and reduced shear +`[-3776, -128, -7360, -3520]` from four retained spatial neighbor samples. The production test requires +decoded `WARPED_CAUSAL` state through `Av1BlockDecoder.DecodeBlock()`, compares every final native Y, +U, and V sample exactly, compares the retained final presentation through the established image-reference +API, runs intrinsic and scalar dispatch through `FeatureTestRunner`, and repeats reconstruction with a +constrained tracked allocator. Separate 8-, 10-, and 12-bit operator tests use an independent scalar +transcription of current libaom's affine loops and all 1,544 current filter coefficients across AVX-512, +AVX, 128-bit, and scalar execution. ## Global warped-motion fixture