// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Buffers.Binary; using System.Text; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Heif; using SixLabors.ImageSharp.Formats.Heif.Av1; using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Cicp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.Memory; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; /// /// Validates complete AV1 reconstruction against independently decoded native component planes. /// [Trait("Format", "Avif")] public class Av1ReconstructionConformanceTests { /// /// The width and height of one CDEF unit in 4x4 luma mode-information units. /// private const int CdefUnitModeInfoSize = 16; /// /// The hardware configurations covering normal SIMD dispatch and the scalar fallback. /// private const HwIntrinsics ReconstructionConfigurations = HwIntrinsics.AllowAll | HwIntrinsics.DisableHWIntrinsic; /// /// The hardware configurations covering the 256-bit, 128-bit, and scalar palette-reconstruction paths. /// private const HwIntrinsics PaletteConfigurations = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic; /// /// The hardware configurations covering normal dispatch, narrower vector fallbacks, and scalar intra-block copy. /// private const HwIntrinsics IntraBlockCopyConfigurations = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic; /// /// The hardware configurations covering the narrower vector widths and scalar fallback for the profile matrix. /// private const HwIntrinsics ProfileFallbackConfigurations = HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic; /// /// The hardware configurations covering the 128-bit and scalar lossless inverse-transform paths. /// private const HwIntrinsics LosslessConfigurations = HwIntrinsics.AllowAll | HwIntrinsics.DisableHWIntrinsic; /// /// The hardware configurations covering the 256-bit, 128-bit, and scalar loop-restoration paths. /// private const HwIntrinsics LoopRestorationConfigurations = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic; /// /// The coverage bit representing an active Wiener restoration unit. /// private const int WienerRestorationCoverage = 1 << (int)Av1RestorationFilterType.Wiener; /// /// The coverage bit representing an active self-guided restoration unit. /// private const int SelfGuidedRestorationCoverage = 1 << (int)Av1RestorationFilterType.SgrProjection; /// /// The coverage bit representing luma palette prediction. /// private const int LumaPaletteCoverage = 1 << 0; /// /// The coverage bit representing chroma palette prediction. /// private const int ChromaPaletteCoverage = 1 << 1; /// /// The luma and chroma syntax coverage required from the independent palette fixture. /// private const int RequiredPaletteCoverage = LumaPaletteCoverage | ChromaPaletteCoverage; /// /// The bit mask containing every AV1 partition type defined for a coding block. /// private const int RequiredPartitionCoverage = (1 << ((int)Av1PartitionType.Vertical4 + 1)) - 1; /// /// The displayed width shared by the independent lossless fixtures. /// private const int LosslessFixtureWidth = 100; /// /// The displayed height shared by the independent lossless fixtures. /// private const int LosslessFixtureHeight = 60; /// /// The displayed width shared by the independent AV1 profile fixtures. /// private const int ProfileFixtureWidth = 512; /// /// The displayed height shared by the independent AV1 profile fixtures. /// private const int ProfileFixtureHeight = 256; /// /// The displayed width of the independent two-layer progressive fixture. /// private const int ProgressiveFixtureWidth = 33; /// /// The displayed height of the independent two-layer progressive fixture. /// private const int ProgressiveFixtureHeight = 11; /// /// The byte length of the fixture's base layer as declared by its a1lx property. /// private const int ProgressiveFirstLayerSize = 55; /// /// The hardware configurations covering the available vector widths and the scalar color-conversion fallback. /// private const HwIntrinsics PresentationConfigurations = HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic; /// /// Verifies deblocking syntax, filter activation, component traversal, and presentation for real eight-, ten-, /// and twelve-bit AV1 and AVIF content. /// [Fact] public void DecodeMatchesPinnedLibaomReference() { ValidateFixture( TestImages.Heif.Av1Deblocking8BitAvif, TestImages.Heif.Av1Deblocking8BitPayload, TestImages.Heif.Av1Deblocking8BitReference, 768, 512, Av1BitDepth.EightBit, Av1ColorFormat.Yuv420, HeifBitDepth.Bit8); ValidateFixture( TestImages.Heif.Av1Deblocking10BitAvif, TestImages.Heif.Av1Deblocking10BitPayload, TestImages.Heif.Av1Deblocking10BitReference, 1024, 428, Av1BitDepth.TenBit, Av1ColorFormat.Yuv444, HeifBitDepth.Bit10); ValidateNativeFixture( TestImages.Heif.Av1Deblocking12BitPayload, TestImages.Heif.Av1Deblocking12BitReference, 1024, 428, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv444, requireActiveCdef: false); ValidatePresentedImage(TestImages.Heif.Av1Deblocking12BitAvif, 64, 64, HeifBitDepth.Bit12); } /// /// Verifies active CDEF syntax, strength selection, unit traversal, subsampling, frame edges, and final native /// samples against scalar libaom for independently encoded eight-, ten-, and twelve-bit still-picture streams /// under normal SIMD dispatch and with hardware intrinsics disabled. /// [Fact] public void DecodeWithActiveCdefMatchesPinnedLibaomReference() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateActiveCdefFixtures, ReconstructionConfigurations); /// /// Verifies exact presented pixels and public metadata for independently encoded eight-, ten-, and twelve-bit /// active-CDEF AVIF images across the available vector widths and the scalar fallback. /// [Fact] public void DecodeWithActiveCdefMatchesPinnedLibavifPresentation() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidatePresentedFixtures, PresentationConfigurations); /// /// Verifies exact native reconstruction for every valid AV1 profile, bit-depth, and chroma-format combination /// supported by AVIF across every available vector width and the scalar fallback. /// [Fact] public void DecodeProfileMatrixMatchesPinnedLibaomReference() => ValidateProfileNativeFixtures(); /// /// Verifies exact native reconstruction for every valid AV1 profile, bit-depth, and chroma-format combination /// under each narrower vector width and the scalar fallback. /// [Fact] public void DecodeProfileMatrixFallbacksMatchPinnedLibaomReference() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateProfileNativeFixtures, ProfileFallbackConfigurations); /// /// Verifies exact presented pixels, public bit-depth metadata, and CICP signaling for every valid AV1 profile, /// bit-depth, and chroma-format combination supported by AVIF. /// [Fact] public void DecodeProfileMatrixMatchesPinnedLibavifPresentation() => ValidateProfilePresentedFixtures(); /// /// Verifies exact presented pixels, public bit-depth metadata, and CICP signaling under each narrower vector width /// and the scalar fallback. /// [Fact] public void DecodeProfileMatrixFallbacksMatchPinnedLibavifPresentation() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateProfilePresentedFixtures, ProfileFallbackConfigurations); /// /// Verifies decoded luma and chroma palette syntax and exact native samples against scalar libaom for an /// independently encoded AV1 still-picture stream. /// [Fact] public void DecodeWithPaletteMatchesPinnedLibaomReference() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidatePaletteNativeFixture, PaletteConfigurations); /// /// Verifies that a real palette frame whose tile entropy payload ends early is rejected instead of being decoded /// from the range decoder's implicit zero padding. /// [Fact] public void DecodeFrameBufferRejectsTruncatedPaletteTileEntropy() { const int TruncatedTileByteCount = 8; byte[] validPayload = TestFile.Create(TestImages.Heif.Av1Palette8BitPayload).Bytes; int obuOffset = 0; int finalObuOffset = 0; int finalSizeFieldOffset = 0; int finalSizeFieldLength = 0; ulong finalPayloadLength = 0; while (obuOffset < validPayload.Length) { byte obuHeader = validPayload[obuOffset]; Assert.True((obuHeader & 0x02) != 0); int headerLength = 1 + ((obuHeader >> 2) & 1); int sizeFieldOffset = obuOffset + headerLength; Av1BitStreamReader sizeReader = new(validPayload.AsSpan(sizeFieldOffset)); ulong payloadLength = sizeReader.ReadLittleEndianBytes128(out int sizeFieldLength); int nextObuOffset = checked(sizeFieldOffset + sizeFieldLength + (int)payloadLength); if (nextObuOffset == validPayload.Length) { finalObuOffset = obuOffset; finalSizeFieldOffset = sizeFieldOffset; finalSizeFieldLength = sizeFieldLength; finalPayloadLength = payloadLength; } obuOffset = nextObuOffset; } Assert.Equal(ObuType.Frame, (ObuType)((validPayload[finalObuOffset] >> 3) & 0x0F)); Assert.Equal(1, finalSizeFieldLength); Assert.InRange(finalPayloadLength, (ulong)(TruncatedTileByteCount + 1), 0x7FUL); byte[] truncatedPayload = validPayload[..^TruncatedTileByteCount]; truncatedPayload[finalSizeFieldOffset] = (byte)(finalPayloadLength - TruncatedTileByteCount); using Av1Decoder decoder = new(Configuration.Default); Assert.Throws( () => decoder.DecodeFrameBuffer(truncatedPayload, null, null, out _).Dispose()); Assert.Null(decoder.SequenceHeader); Assert.Null(decoder.FrameHeader); Assert.Null(decoder.FrameInfo); } /// /// Verifies decoded luma and chroma palette syntax and exact presented pixels for an independently encoded AVIF /// image across the available vector widths and the scalar fallback. /// [Fact] public void DecodeWithPaletteMatchesPinnedLibavifPresentation() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidatePalettePresentedFixture, PresentationConfigurations); /// /// Verifies that malformed data following a decoded palette tile releases its frame state before the same decoder /// processes another payload. /// [Fact] public void DecodeFrameBufferRecoversAfterMalformedFollowingObu() { byte[] validPayload = TestFile.Create(TestImages.Heif.Av1Palette8BitPayload).Bytes; // The palette fixture ends with one combined-frame OBU containing one tile, so the intact prefix creates and // completes a real Av1TileReader. The appended padding OBU declares one zero byte; AV1 padding requires a // trailing-one bit, making this later bounded-payload failure deterministic without corrupting tile entropy. byte[] malformedPayload = [ .. validPayload, 0x7A, // Padding OBU with an explicit payload-size field. 0x01, // LEB128 payload length of one byte. 0x00, // Invalid padding payload with no trailing-one bit. ]; using Av1Decoder decoder = new(Configuration.Default); Assert.Throws( () => decoder.DecodeFrameBuffer(malformedPayload, null, null, out _).Dispose()); Assert.Null(decoder.SequenceHeader); Assert.Null(decoder.FrameHeader); Assert.Null(decoder.FrameInfo); using Av1FrameBuffer recoveredFrameBuffer = decoder.DecodeFrameBuffer(validPayload, null, null, out _); Assert.Equal(33, recoveredFrameBuffer.Width); Assert.Equal(11, recoveredFrameBuffer.Height); Assert.Equal(RequiredPaletteCoverage, GetPaletteCoverage(decoder)); } /// /// Verifies selected intra-block-copy prediction and exact native samples against scalar libaom for an /// independently encoded AV1 still pictures across every available vector width and the scalar fallback. /// [Fact] public void DecodeWithIntraBlockCopyMatchesPinnedLibaomReference() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateIntraBlockCopyNativeFixtures, IntraBlockCopyConfigurations); /// /// Verifies exact presented pixels for independently encoded intra-block-copy AVIF images across the available /// vector widths and the scalar fallback. /// [Fact] public void DecodeWithIntraBlockCopyMatchesPinnedLibavifPresentation() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateIntraBlockCopyPresentedFixtures, PresentationConfigurations); /// /// Verifies the production single-reference inter-reconstruction path against exact native and presentation /// references across the available vector widths and scalar fallback. /// [Fact] public void DecodeProgressiveSingleReferenceMatchesPinnedReferences() => FeatureTestRunner.RunWithHwIntrinsicsFeature( ValidateProgressiveSingleReferenceFixtureWithDefaultConfiguration, ReconstructionConfigurations); /// /// Verifies production single-reference inter reconstruction with a constrained allocator. /// [Fact] public void DecodeProgressiveSingleReferenceWithConstrainedAllocator() { TestMemoryAllocator allocator = new() { BufferCapacityInBytes = 1_024 }; Configuration configuration = Configuration.Default.Clone(); configuration.MemoryAllocator = allocator; ValidateProgressiveSingleReferenceFixture(configuration, verifyPresentation: false); } /// /// Verifies that the production dependent-frame result owns its motion-field storage until decoder disposal. /// [Fact] public void DecodeProgressiveSingleReferenceTracksMotionFieldResultOwnership() { TestMemoryAllocator allocator = new(); allocator.EnableNonThreadSafeLogging(); Configuration configuration = Configuration.Default.Clone(); configuration.MemoryAllocator = allocator; byte[] payload = TestFile.Create(TestImages.Heif.Av1Progressive8BitPayload).Bytes; using Av1Decoder decoder = new(configuration); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer( payload, null, null, out _, new Av1LayeredImageIndex(ProgressiveFirstLayerSize, 0, 0)); TestMemoryAllocator.AllocationRequest retainedMotionField = Assert.Single( allocator.AllocationLog, request => request.ElementType.Name == "RetainedMotionFieldEntry"); TestMemoryAllocator.AllocationRequest temporalMotionField = Assert.Single( allocator.AllocationLog, request => request.ElementType.Name == "TemporalMotionFieldEntry"); // Reference-slot and presentation owners are released while DecodeFrameBuffer transfers the native planes. // The decoder's inspectable FrameInfo result remains the final motion-field owner until decoder disposal. Assert.DoesNotContain( allocator.ReturnLog, returned => returned.HashCodeOfBuffer == retainedMotionField.HashCodeOfBuffer); Assert.DoesNotContain( allocator.ReturnLog, returned => returned.HashCodeOfBuffer == temporalMotionField.HashCodeOfBuffer); frameBuffer.Dispose(); Assert.DoesNotContain( allocator.ReturnLog, returned => returned.HashCodeOfBuffer == retainedMotionField.HashCodeOfBuffer); Assert.DoesNotContain( allocator.ReturnLog, returned => returned.HashCodeOfBuffer == temporalMotionField.HashCodeOfBuffer); decoder.Dispose(); decoder.Dispose(); Assert.Single( allocator.ReturnLog, returned => returned.HashCodeOfBuffer == retainedMotionField.HashCodeOfBuffer); Assert.Single(allocator.ReturnLog, returned => returned.HashCodeOfBuffer == temporalMotionField.HashCodeOfBuffer); } /// /// Verifies lossless syntax, residual reconstruction, and exact native samples against scalar libaom for /// independently encoded eight-, ten-, and twelve-bit AVIF images. /// [Fact] public void DecodeLosslessMatchesPinnedLibaomReference() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateLosslessFixtures, LosslessConfigurations); /// /// Verifies exact presented pixels for independently encoded lossless eight-, ten-, and twelve-bit AVIF images /// across the available vector widths and the scalar fallback. /// [Fact] public void DecodeLosslessMatchesPinnedLibavifPresentation() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateLosslessPresentedFixtures, PresentationConfigurations); /// /// Verifies active normative super-resolution, chroma-width rounding, replicated edges, and exact native samples /// against scalar libaom for independently encoded eight-, ten-, and twelve-bit still-picture streams. /// [Fact] public void DecodeWithSuperResolutionMatchesPinnedLibaomReference() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateSuperResolutionFixtures, ReconstructionConfigurations); /// /// Verifies exact presented pixels and public metadata for independently packaged eight-, ten-, and twelve-bit /// active-super-resolution AVIF images across the available vector widths and the scalar fallback. /// [Fact] public void DecodeWithSuperResolutionMatchesPinnedLibavifPresentation() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateSuperResolutionPresentedFixtures, PresentationConfigurations); /// /// Verifies active normative loop restoration and exact native samples against scalar libaom for independently /// encoded eight-, ten-, and twelve-bit still-picture streams. /// [Fact] public void DecodeWithLoopRestorationMatchesPinnedLibaomReference() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateLoopRestorationFixtures, LoopRestorationConfigurations); /// /// Verifies combined super-resolution and loop-restoration geometry for independently encoded 8-bit 4:2:0 content. /// [Fact] public void DecodeWithLoopRestorationAndSuperResolutionMatchesPinnedLibaomReference8Bit420() => FeatureTestRunner.RunWithHwIntrinsicsFeature( ValidateLoopRestorationAndSuperResolution8Bit420, LoopRestorationConfigurations); /// /// Verifies combined super-resolution and loop-restoration geometry for independently encoded 10-bit 4:2:2 content. /// [Fact] public void DecodeWithLoopRestorationAndSuperResolutionMatchesPinnedLibaomReference10Bit422() => FeatureTestRunner.RunWithHwIntrinsicsFeature( ValidateLoopRestorationAndSuperResolution10Bit422, LoopRestorationConfigurations); /// /// Verifies combined super-resolution and loop-restoration geometry for independently encoded 12-bit 4:4:4 content. /// [Fact] public void DecodeWithLoopRestorationAndSuperResolutionMatchesPinnedLibaomReference12Bit444() => FeatureTestRunner.RunWithHwIntrinsicsFeature( ValidateLoopRestorationAndSuperResolution12Bit444, LoopRestorationConfigurations); /// /// Verifies exact presented pixels and public metadata for independently encoded eight-, ten-, and twelve-bit /// active-restoration AVIF images across the available vector widths and the scalar fallback. /// [Fact] public void DecodeWithLoopRestorationMatchesPinnedLibavifPresentation() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateRestorationPresentedFixtures, PresentationConfigurations); /// /// Verifies that the independently encoded AVIF presentation fixtures collectively select both restoration algorithms. /// [Fact] public void LoopRestorationPresentationFixturesSelectBothAlgorithms() { int restorationCoverage = GetRestorationCoverageFromAvif(TestFile.Create(TestImages.Heif.Av1Restoration8BitAvif).Bytes); restorationCoverage |= GetRestorationCoverageFromAvif(TestFile.Create(TestImages.Heif.Av1Restoration10BitAvif).Bytes); restorationCoverage |= GetRestorationCoverageFromAvif(TestFile.Create(TestImages.Heif.Av1Restoration12BitAvif).Bytes); int requiredCoverage = WienerRestorationCoverage | SelfGuidedRestorationCoverage; Assert.Equal(requiredCoverage, restorationCoverage & requiredCoverage); } /// /// Verifies film-grain template generation, block selection, overlap, chroma scaling, subsampling, high-bit-depth /// arithmetic, and exact native presentation samples against scalar libaom. /// [Fact] public void DecodeWithFilmGrainMatchesPinnedLibaomReference() => FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidateFilmGrainFixtures, LoopRestorationConfigurations); /// /// Verifies that independently encoded AV1 streams exercise every normative coding-block partition shape. /// [Fact] public void IndependentFixturesCoverEveryPartitionType() { int coverage = GetPartitionCoverage(TestImages.Heif.Av1Cdef8BitPayload); coverage |= GetPartitionCoverage(TestImages.Heif.Av1Cdef10BitPayload); coverage |= GetPartitionCoverage(TestImages.Heif.Av1Cdef12BitPayload); Assert.Equal(RequiredPartitionCoverage, coverage & RequiredPartitionCoverage); } /// /// Validates every native profile fixture under the hardware configuration selected by /// . /// private static void ValidateProfileNativeFixtures() { ValidateProfileNativeFixture( TestImages.Heif.Av1Profile8BitMonochromeAvif, TestImages.Heif.Av1Profile8BitMonochromeReference, Av1BitDepth.EightBit, Av1ColorFormat.Yuv400, ObuSequenceProfile.Main); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile8Bit420Avif, TestImages.Heif.Av1Profile8Bit420Reference, Av1BitDepth.EightBit, Av1ColorFormat.Yuv420, ObuSequenceProfile.Main); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile8Bit422Avif, TestImages.Heif.Av1Profile8Bit422Reference, Av1BitDepth.EightBit, Av1ColorFormat.Yuv422, ObuSequenceProfile.Professional); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile8Bit444Avif, TestImages.Heif.Av1Profile8Bit444Reference, Av1BitDepth.EightBit, Av1ColorFormat.Yuv444, ObuSequenceProfile.High); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile10BitMonochromeAvif, TestImages.Heif.Av1Profile10BitMonochromeReference, Av1BitDepth.TenBit, Av1ColorFormat.Yuv400, ObuSequenceProfile.Main); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile10Bit420Avif, TestImages.Heif.Av1Profile10Bit420Reference, Av1BitDepth.TenBit, Av1ColorFormat.Yuv420, ObuSequenceProfile.Main); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile10Bit422Avif, TestImages.Heif.Av1Profile10Bit422Reference, Av1BitDepth.TenBit, Av1ColorFormat.Yuv422, ObuSequenceProfile.Professional); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile10Bit444Avif, TestImages.Heif.Av1Profile10Bit444Reference, Av1BitDepth.TenBit, Av1ColorFormat.Yuv444, ObuSequenceProfile.High); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile12BitMonochromeAvif, TestImages.Heif.Av1Profile12BitMonochromeReference, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv400, ObuSequenceProfile.Professional); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile12Bit420Avif, TestImages.Heif.Av1Profile12Bit420Reference, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv420, ObuSequenceProfile.Professional); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile12Bit422Avif, TestImages.Heif.Av1Profile12Bit422Reference, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv422, ObuSequenceProfile.Professional); ValidateProfileNativeFixture( TestImages.Heif.Av1Profile12Bit444Avif, TestImages.Heif.Av1Profile12Bit444Reference, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv444, ObuSequenceProfile.Professional); } /// /// Validates every presented profile fixture under the hardware configuration selected by /// . /// private static void ValidateProfilePresentedFixtures() { ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile8BitMonochromeAvif, TestImages.Heif.Av1Profile8BitMonochromePresentationReference, HeifBitDepth.Bit8); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile8Bit420Avif, TestImages.Heif.Av1Profile8Bit420PresentationReference, HeifBitDepth.Bit8); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile8Bit422Avif, TestImages.Heif.Av1Profile8Bit422PresentationReference, HeifBitDepth.Bit8); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile8Bit444Avif, TestImages.Heif.Av1Profile8Bit444PresentationReference, HeifBitDepth.Bit8); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile10BitMonochromeAvif, TestImages.Heif.Av1Profile10BitMonochromePresentationReference, HeifBitDepth.Bit10); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile10Bit420Avif, TestImages.Heif.Av1Profile10Bit420PresentationReference, HeifBitDepth.Bit10); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile10Bit422Avif, TestImages.Heif.Av1Profile10Bit422PresentationReference, HeifBitDepth.Bit10); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile10Bit444Avif, TestImages.Heif.Av1Profile10Bit444PresentationReference, HeifBitDepth.Bit10); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile12BitMonochromeAvif, TestImages.Heif.Av1Profile12BitMonochromePresentationReference, HeifBitDepth.Bit12); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile12Bit420Avif, TestImages.Heif.Av1Profile12Bit420PresentationReference, HeifBitDepth.Bit12); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile12Bit422Avif, TestImages.Heif.Av1Profile12Bit422PresentationReference, HeifBitDepth.Bit12); ValidateProfilePresentedFixture( TestImages.Heif.Av1Profile12Bit444Avif, TestImages.Heif.Av1Profile12Bit444PresentationReference, HeifBitDepth.Bit12); } /// /// Validates one independently encoded AVIF against its native Y4M reference and signaled sequence profile. /// /// The complete AVIF container. /// The native Y4M output produced by the pinned scalar libaom-backed decoder. /// The expected AV1 sample precision. /// The expected native chroma-sampling layout. /// The AV1 profile required by the bit-depth and chroma-format combination. private static void ValidateProfileNativeFixture( string imagePath, string referencePath, Av1BitDepth bitDepth, Av1ColorFormat colorFormat, ObuSequenceProfile sequenceProfile) { byte[] imageBytes = TestFile.Create(imagePath).Bytes; byte[] referenceBytes = TestFile.Create(referencePath).Bytes; (string chromaTag, string extendedChromaTag) = GetY4mColorSpace(bitDepth, colorFormat); string expectedHeader = $"YUV4MPEG2 W{ProfileFixtureWidth} H{ProfileFixtureHeight} F25:1 Ip A0:0 C{chromaTag} XYSCSS={extendedChromaTag} XCOLORRANGE=FULL\n"; int headerTerminator = referenceBytes.AsSpan().IndexOf((byte)'\n'); Assert.NotEqual(-1, headerTerminator); int fileHeaderLength = headerTerminator + 1; Assert.Equal(expectedHeader, Encoding.ASCII.GetString(referenceBytes, 0, fileHeaderLength)); ReadOnlySpan nativeReference = referenceBytes.AsSpan(fileHeaderLength); ReadOnlySpan frameHeader = "FRAME\n"u8; Assert.True(nativeReference.StartsWith(frameHeader)); nativeReference = nativeReference[frameHeader.Length..]; Span payload = GetSoleAv1ItemPayload(imageBytes); using Av1Decoder decoder = new(Configuration.Default); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _); Assert.Equal(ProfileFixtureWidth, frameBuffer.Width); Assert.Equal(ProfileFixtureHeight, frameBuffer.Height); Assert.Equal(bitDepth, frameBuffer.BitDepth); Assert.Equal(colorFormat, frameBuffer.ColorFormat); ObuSequenceHeader sequenceHeader = Assert.IsType(decoder.SequenceHeader); ObuColorConfig colorConfig = sequenceHeader.ColorConfig; Assert.Equal(sequenceProfile, sequenceHeader.SequenceProfile); Assert.Equal(bitDepth, colorConfig.BitDepth); Assert.Equal(colorFormat, colorConfig.GetColorFormat()); Assert.Equal(colorFormat == Av1ColorFormat.Yuv400, colorConfig.IsMonochrome); Assert.True(colorConfig.IsColorDescriptionPresent); Assert.Equal(ObuColorPrimaries.Bt709, colorConfig.ColorPrimaries); Assert.Equal(ObuTransferCharacteristics.Srgb, colorConfig.TransferCharacteristics); Assert.Equal(ObuMatrixCoefficients.Bt601, colorConfig.MatrixCoefficients); Assert.True(colorConfig.ColorRange); AssertNativePlanesEqual(decoder, frameBuffer, nativeReference); } /// /// Validates the exact public presentation and metadata of one independently encoded AVIF profile fixture. /// /// The complete AVIF container. /// The eight-bit RGBA output produced by the pinned scalar libavif decoder. /// The expected public HEIF sample precision. private static void ValidateProfilePresentedFixture(string imagePath, string referencePath, HeifBitDepth metadataBitDepth) { DecoderOptions options = new() { MaxFrames = 1 }; byte[] imageBytes = TestFile.Create(imagePath).Bytes; byte[] referenceBytes = TestFile.Create(referencePath).Bytes; using Image image = Image.Load(options, imageBytes); using Image reference = Image.Load(referenceBytes); Assert.Equal(ProfileFixtureWidth, image.Width); Assert.Equal(ProfileFixtureHeight, image.Height); Assert.Single(image.Frames); HeifMetadata metadata = image.Metadata.GetHeifMetadata(); Assert.Equal(HeifCompressionMethod.Av1, metadata.CompressionMethod); Assert.Equal(metadataBitDepth, metadata.BitDepth); CicpProfile colorProfile = Assert.IsType(image.Metadata.CicpProfile); Assert.Equal(CicpColorPrimaries.ItuRBt709_6, colorProfile.ColorPrimaries); Assert.Equal(CicpTransferCharacteristics.Iec61966_2_1, colorProfile.TransferCharacteristics); Assert.Equal(CicpMatrixCoefficients.ItuRBt601_7_525, colorProfile.MatrixCoefficients); Assert.True(colorProfile.FullRange); ImageComparer.Exact.VerifySimilarity(reference, image); } /// /// Gets the Y4M chroma tags that encode one AV1 bit-depth and sampling-layout combination. /// /// The encoded AV1 sample precision. /// The encoded AV1 chroma-sampling layout. /// The Y4M C tag and extended XYSCSS tag. private static (string ChromaTag, string ExtendedChromaTag) GetY4mColorSpace(Av1BitDepth bitDepth, Av1ColorFormat colorFormat) { // Y4M uses a legacy 420jpeg name at eight bits, lowercase p in high-depth C tags, and uppercase P in the // corresponding XYSCSS tags. Keeping the exact spellings detects a reference generated with different layout. return (bitDepth, colorFormat) switch { (Av1BitDepth.EightBit, Av1ColorFormat.Yuv400) => ("mono", "400"), (Av1BitDepth.EightBit, Av1ColorFormat.Yuv420) => ("420jpeg", "420JPEG"), (Av1BitDepth.EightBit, Av1ColorFormat.Yuv422) => ("422", "422"), (Av1BitDepth.EightBit, Av1ColorFormat.Yuv444) => ("444", "444"), (Av1BitDepth.TenBit, Av1ColorFormat.Yuv400) => ("mono10", "400"), (Av1BitDepth.TenBit, Av1ColorFormat.Yuv420) => ("420p10", "420P10"), (Av1BitDepth.TenBit, Av1ColorFormat.Yuv422) => ("422p10", "422P10"), (Av1BitDepth.TenBit, Av1ColorFormat.Yuv444) => ("444p10", "444P10"), (Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv400) => ("mono12", "400"), (Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv420) => ("420p12", "420P12"), (Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv422) => ("422p12", "422P12"), _ => ("444p12", "444P12") }; } /// /// Validates every active-CDEF fixture under the hardware configuration selected by . /// private static void ValidateActiveCdefFixtures() { ValidateActiveCdefFixture( TestImages.Heif.Av1Cdef8BitPayload, TestImages.Heif.Av1Cdef8BitReference, 768, 512, Av1BitDepth.EightBit, Av1ColorFormat.Yuv420); ValidateActiveCdefFixture( TestImages.Heif.Av1Cdef10BitPayload, TestImages.Heif.Av1Cdef10BitReference, 1024, 428, Av1BitDepth.TenBit, Av1ColorFormat.Yuv444); ValidateActiveCdefFixture( TestImages.Heif.Av1Cdef12BitPayload, TestImages.Heif.Av1Cdef12BitReference, 1024, 428, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv444); } /// /// Validates every active-CDEF presentation fixture under the hardware configuration selected by /// . /// private static void ValidatePresentedFixtures() { ValidatePresentedFixture( TestImages.Heif.Av1Cdef8BitAvif, TestImages.Heif.Av1Cdef8BitPresentationReference, 768, 512, HeifBitDepth.Bit8); ValidatePresentedFixture( TestImages.Heif.Av1Cdef10BitAvif, TestImages.Heif.Av1Cdef10BitPresentationReference, 1024, 428, HeifBitDepth.Bit10); ValidatePresentedFixture( TestImages.Heif.Av1Cdef12BitAvif, TestImages.Heif.Av1Cdef12BitPresentationReference, 1024, 428, HeifBitDepth.Bit12); } /// /// Validates the active-palette presentation fixture under the hardware configuration selected by /// . /// private static void ValidatePalettePresentedFixture() => ValidatePresentedFixture( TestImages.Heif.Av1Palette8BitAvif, TestImages.Heif.Av1Palette8BitPresentationReference, 33, 11, HeifBitDepth.Bit8, requirePalette: true); /// /// Validates the active-palette native fixture under the hardware configuration selected by /// . /// private static void ValidatePaletteNativeFixture() => ValidateNativeFixture( TestImages.Heif.Av1Palette8BitPayload, TestImages.Heif.Av1Palette8BitReference, 33, 11, Av1BitDepth.EightBit, Av1ColorFormat.Yuv444, requireActiveCdef: false, requireActiveLoopFilter: false, requirePalette: true); /// /// Validates every active intra-block-copy native fixture under the hardware configuration selected by /// . /// private static void ValidateIntraBlockCopyNativeFixtures() { ValidateIntraBlockCopyNativeFixture( TestImages.Heif.Av1IntraBlockCopy8BitAvif, TestImages.Heif.Av1IntraBlockCopy8BitReference, Av1BitDepth.EightBit); ValidateIntraBlockCopyNativeFixture( TestImages.Heif.Av1IntraBlockCopy10BitAvif, TestImages.Heif.Av1IntraBlockCopy10BitReference, Av1BitDepth.TenBit); ValidateIntraBlockCopyNativeFixture( TestImages.Heif.Av1IntraBlockCopy12BitAvif, TestImages.Heif.Av1IntraBlockCopy12BitReference, Av1BitDepth.TwelveBit); } /// /// Validates one independently encoded intra-block-copy AVIF against its native Y4M reference. /// /// The complete AVIF container. /// The native Y4M output produced by the pinned scalar libaom-backed decoder. /// The expected AV1 sample precision. private static void ValidateIntraBlockCopyNativeFixture(string imagePath, string referencePath, Av1BitDepth bitDepth) { byte[] imageBytes = TestFile.Create(imagePath).Bytes; byte[] referenceBytes = TestFile.Create(referencePath).Bytes; ReadOnlySpan fileHeader = bitDepth switch { Av1BitDepth.EightBit => "YUV4MPEG2 W512 H256 F25:1 Ip A0:0 C444 XYSCSS=444 XCOLORRANGE=FULL\n"u8, Av1BitDepth.TenBit => "YUV4MPEG2 W512 H256 F25:1 Ip A0:0 C444p10 XYSCSS=444P10 XCOLORRANGE=FULL\n"u8, _ => "YUV4MPEG2 W512 H256 F25:1 Ip A0:0 C444p12 XYSCSS=444P12 XCOLORRANGE=FULL\n"u8 }; ReadOnlySpan frameHeader = "FRAME\n"u8; // The retained Y4M header locks the independently decoded reference to the expected dimensions, sampling, // bit depth, and full range. Only the following frame payload contains the planar Y, U, and V samples. ReadOnlySpan nativeReference = referenceBytes; Assert.True(nativeReference.StartsWith(fileHeader)); nativeReference = nativeReference[fileHeader.Length..]; Assert.True(nativeReference.StartsWith(frameHeader)); nativeReference = nativeReference[frameHeader.Length..]; Span payload = GetSoleAv1ItemPayload(imageBytes); using Av1Decoder decoder = new(Configuration.Default); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _); Assert.Equal(512, frameBuffer.Width); Assert.Equal(256, frameBuffer.Height); Assert.Equal(bitDepth, frameBuffer.BitDepth); Assert.Equal(Av1ColorFormat.Yuv444, frameBuffer.ColorFormat); Assert.NotNull(decoder.FrameHeader); Assert.True(decoder.FrameHeader.AllowIntraBlockCopy); Assert.NotEqual(0, GetIntraBlockCopyBlockCount(decoder)); AssertNativePlanesEqual(decoder, frameBuffer, nativeReference); } /// /// Validates every active intra-block-copy presentation fixture under the hardware configuration selected by /// . /// private static void ValidateIntraBlockCopyPresentedFixtures() { ValidatePresentedFixture( TestImages.Heif.Av1IntraBlockCopy8BitAvif, TestImages.Heif.Av1IntraBlockCopy8BitPresentationReference, 512, 256, HeifBitDepth.Bit8, requireIntraBlockCopy: true); ValidatePresentedFixture( TestImages.Heif.Av1IntraBlockCopy10BitAvif, TestImages.Heif.Av1IntraBlockCopy10BitPresentationReference, 512, 256, HeifBitDepth.Bit10, requireIntraBlockCopy: true); ValidatePresentedFixture( TestImages.Heif.Av1IntraBlockCopy12BitAvif, TestImages.Heif.Av1IntraBlockCopy12BitPresentationReference, 512, 256, HeifBitDepth.Bit12, requireIntraBlockCopy: true); } /// /// Runs the exact final-layer native and presentation comparisons with the default configuration. /// private static void ValidateProgressiveSingleReferenceFixtureWithDefaultConfiguration() => ValidateProgressiveSingleReferenceFixture(Configuration.Default, verifyPresentation: true); /// /// Verifies the final dependent layer with the requested allocator. /// /// The decoder configuration. /// Whether to verify the final public RGBA presentation. private static void ValidateProgressiveSingleReferenceFixture( Configuration configuration, bool verifyPresentation) { byte[] payload = TestFile.Create(TestImages.Heif.Av1Progressive8BitPayload).Bytes; byte[] referenceBytes = TestFile.Create(TestImages.Heif.Av1Progressive8BitReference).Bytes; ReadOnlySpan fileHeader = "YUV4MPEG2 W33 H11 F25:1 Ip A0:0 C444alpha XYSCSS=444 XCOLORRANGE=FULL\n"u8; ReadOnlySpan frameHeader = "FRAME\n"u8; int planeSampleCount = ProgressiveFixtureWidth * ProgressiveFixtureHeight; int frameSampleCount = planeSampleCount * 4; // The pinned reference contains both progressive YUV444-alpha outputs in decode order. Select the second frame // so this assertion cannot pass by comparing only the independently decodable base layer. ReadOnlySpan nativeReference = referenceBytes; Assert.True(nativeReference.StartsWith(fileHeader)); nativeReference = nativeReference[fileHeader.Length..]; Assert.True(nativeReference.StartsWith(frameHeader)); int storedFrameSize = frameHeader.Length + frameSampleCount; Assert.Equal(storedFrameSize * 2, nativeReference.Length); ReadOnlySpan finalFrameReference = nativeReference[storedFrameSize..]; Assert.True(finalFrameReference.StartsWith(frameHeader)); finalFrameReference = finalFrameReference[frameHeader.Length..]; Assert.Equal(frameSampleCount, finalFrameReference.Length); // The Y4M stores the color item's Y, U, and V planes before the auxiliary alpha plane. Native AV1 reconstruction // is compared with exactly those first three planes of the final dependent frame. ReadOnlySpan colorReference = finalFrameReference[..(planeSampleCount * 3)]; using Av1Decoder decoder = new(configuration); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer( payload, null, null, out _, new Av1LayeredImageIndex(ProgressiveFirstLayerSize, 0, 0)); Assert.Equal(ProgressiveFixtureWidth, frameBuffer.Width); Assert.Equal(ProgressiveFixtureHeight, frameBuffer.Height); Assert.Equal(Av1BitDepth.EightBit, frameBuffer.BitDepth); Assert.Equal(Av1ColorFormat.Yuv444, frameBuffer.ColorFormat); Assert.Equal(1, frameBuffer.BufferY!.FastMemoryGroup.Count); Assert.Equal(1, frameBuffer.BufferCb!.FastMemoryGroup.Count); Assert.Equal(1, frameBuffer.BufferCr!.FastMemoryGroup.Count); ObuSequenceHeader sequenceHeader = Assert.IsType(decoder.SequenceHeader); ObuFrameHeader finalFrameHeader = Assert.IsType(decoder.FrameHeader); Av1FrameInfo frameInfo = Assert.IsType(decoder.FrameInfo); Assert.Equal(ObuFrameType.InterFrame, finalFrameHeader.FrameType); int superblockSizeLog2 = sequenceHeader.SuperblockSizeLog2; int superblockColumnCount = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameWidth, superblockSizeLog2) >> superblockSizeLog2; int superblockRowCount = Av1Math.AlignPowerOf2(sequenceHeader.MaxFrameHeight, superblockSizeLog2) >> superblockSizeLog2; int interBlockCount = 0; // Traverse the final coding-block records once rather than revisiting every 4x4 map cell covered by each // block. The syntax assertions ensure that this fixture reaches only the completed single-reference path. for (int superblockRow = 0; superblockRow < superblockRowCount; superblockRow++) { for (int superblockColumn = 0; superblockColumn < superblockColumnCount; superblockColumn++) { Av1SuperblockInfo superblock = frameInfo.GetSuperblock(new Point(superblockColumn, superblockRow)); foreach (Av1BlockModeInfo modeInfo in superblock.GetModeInfos()) { if (modeInfo.ReferenceFrames[0] < Av1ReferenceFrameType.Last) { continue; } Assert.Equal(Av1ReferenceFrameType.None, modeInfo.ReferenceFrames[1]); Assert.Equal(Av1MotionMode.SimpleTranslation, modeInfo.MotionMode); interBlockCount++; } } } Assert.NotEqual(0, interBlockCount); AssertNativePlanesEqual(decoder, frameBuffer, colorReference); if (!verifyPresentation) { return; } DecoderOptions options = new() { Configuration = configuration, MaxFrames = 1 }; byte[] imageBytes = TestFile.Create(TestImages.Heif.Av1Progressive8BitAvif).Bytes; byte[] presentationBytes = TestFile.Create(TestImages.Heif.Av1Progressive8BitPresentationReference).Bytes; using Image image = Image.Load(options, imageBytes); using Image presentationReference = Image.Load(presentationBytes); Assert.Equal(ProgressiveFixtureWidth, image.Width); Assert.Equal(ProgressiveFixtureHeight, image.Height); Assert.Single(image.Frames); Assert.Equal(HeifBitDepth.Bit8, image.Metadata.GetHeifMetadata().BitDepth); ImageComparer.Exact.VerifySimilarity(presentationReference, image); } /// /// Validates every lossless native fixture under the hardware configuration selected by /// . /// private static void ValidateLosslessFixtures() { ValidateLosslessFixture( TestImages.Heif.Av1Lossless8BitAvif, TestImages.Heif.Av1Lossless8BitReference, Av1BitDepth.EightBit); ValidateLosslessFixture( TestImages.Heif.Av1Lossless10BitAvif, TestImages.Heif.Av1Lossless10BitReference, Av1BitDepth.TenBit); ValidateLosslessFixture( TestImages.Heif.Av1Lossless12BitAvif, TestImages.Heif.Av1Lossless12BitReference, Av1BitDepth.TwelveBit); } /// /// Validates every lossless presentation fixture under the hardware configuration selected by /// . /// private static void ValidateLosslessPresentedFixtures() { ValidatePresentedFixture( TestImages.Heif.Av1Lossless8BitAvif, TestImages.Heif.Av1Lossless8BitPresentationReference, LosslessFixtureWidth, LosslessFixtureHeight, HeifBitDepth.Bit8); ValidatePresentedFixture( TestImages.Heif.Av1Lossless10BitAvif, TestImages.Heif.Av1Lossless10BitPresentationReference, LosslessFixtureWidth, LosslessFixtureHeight, HeifBitDepth.Bit10); ValidatePresentedFixture( TestImages.Heif.Av1Lossless12BitAvif, TestImages.Heif.Av1Lossless12BitPresentationReference, LosslessFixtureWidth, LosslessFixtureHeight, HeifBitDepth.Bit12); } /// /// Validates every active super-resolution fixture under the hardware configuration selected by /// . /// private static void ValidateSuperResolutionFixtures() { ValidateSuperResolutionFixture( TestImages.Heif.Av1SuperResolution8BitPayload, TestImages.Heif.Av1SuperResolution8BitReference, 768, 512, Av1BitDepth.EightBit, Av1ColorFormat.Yuv420); ValidateSuperResolutionFixture( TestImages.Heif.Av1SuperResolution10BitPayload, TestImages.Heif.Av1SuperResolution10BitReference, 1024, 428, Av1BitDepth.TenBit, Av1ColorFormat.Yuv444); ValidateSuperResolutionFixture( TestImages.Heif.Av1SuperResolution12BitPayload, TestImages.Heif.Av1SuperResolution12BitReference, 1024, 428, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv444); } /// /// Validates every active-super-resolution presentation fixture under the hardware configuration selected by /// . /// private static void ValidateSuperResolutionPresentedFixtures() { ValidatePresentedFixture( TestImages.Heif.Av1SuperResolution8BitAvif, TestImages.Heif.Av1SuperResolution8BitPresentationReference, 768, 512, HeifBitDepth.Bit8, requireSuperResolution: true); ValidatePresentedFixture( TestImages.Heif.Av1SuperResolution10BitAvif, TestImages.Heif.Av1SuperResolution10BitPresentationReference, 1024, 428, HeifBitDepth.Bit10, requireSuperResolution: true); ValidatePresentedFixture( TestImages.Heif.Av1SuperResolution12BitAvif, TestImages.Heif.Av1SuperResolution12BitPresentationReference, 1024, 428, HeifBitDepth.Bit12, requireSuperResolution: true); } /// /// Validates every active loop-restoration fixture under the hardware configuration selected by /// . /// private static void ValidateLoopRestorationFixtures() { int restorationCoverage = ValidateLoopRestorationFixture( TestImages.Heif.Av1Restoration8BitPayload, TestImages.Heif.Av1Restoration8BitReference, 768, 512, Av1BitDepth.EightBit, Av1ColorFormat.Yuv420); restorationCoverage |= ValidateLoopRestorationFixture( TestImages.Heif.Av1Restoration10BitPayload, TestImages.Heif.Av1Restoration10BitReference, 1024, 428, Av1BitDepth.TenBit, Av1ColorFormat.Yuv444); restorationCoverage |= ValidateLoopRestorationFixture( TestImages.Heif.Av1Restoration12BitPayload, TestImages.Heif.Av1Restoration12BitReference, 1024, 428, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv444); // Exact output only proves both restoration algorithms when the independent fixture set // actually selects at least one unit of each type during every feature-runner invocation. int requiredCoverage = WienerRestorationCoverage | SelfGuidedRestorationCoverage; Assert.Equal(requiredCoverage, restorationCoverage & requiredCoverage); } /// /// Validates active restoration after super-resolution for 8-bit 4:2:0 content. /// private static void ValidateLoopRestorationAndSuperResolution8Bit420() => ValidateLoopRestorationFixture( TestImages.Heif.Av1RestorationSuperResolution8BitPayload, TestImages.Heif.Av1RestorationSuperResolution8BitReference, 768, 512, Av1BitDepth.EightBit, Av1ColorFormat.Yuv420, requireSuperResolution: true); /// /// Validates active restoration after super-resolution for 10-bit 4:2:2 content. /// private static void ValidateLoopRestorationAndSuperResolution10Bit422() => ValidateLoopRestorationFixture( TestImages.Heif.Av1RestorationSuperResolution10BitPayload, TestImages.Heif.Av1RestorationSuperResolution10BitReference, 512, 256, Av1BitDepth.TenBit, Av1ColorFormat.Yuv422, requireSuperResolution: true); /// /// Validates active restoration after super-resolution for 12-bit 4:4:4 content. /// private static void ValidateLoopRestorationAndSuperResolution12Bit444() => ValidateLoopRestorationFixture( TestImages.Heif.Av1RestorationSuperResolution12BitPayload, TestImages.Heif.Av1RestorationSuperResolution12BitReference, 1024, 428, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv444, requireSuperResolution: true); /// /// Validates every active-restoration presentation fixture under the hardware configuration selected by /// . /// private static void ValidateRestorationPresentedFixtures() { ValidatePresentedFixture( TestImages.Heif.Av1Restoration8BitAvif, TestImages.Heif.Av1Restoration8BitPresentationReference, 768, 512, HeifBitDepth.Bit8); ValidatePresentedFixture( TestImages.Heif.Av1Restoration10BitAvif, TestImages.Heif.Av1Restoration10BitPresentationReference, 1024, 428, HeifBitDepth.Bit10); ValidatePresentedFixture( TestImages.Heif.Av1Restoration12BitAvif, TestImages.Heif.Av1Restoration12BitPresentationReference, 1024, 428, HeifBitDepth.Bit12); } /// /// Validates every active film-grain fixture under the hardware configuration selected by /// . /// private static void ValidateFilmGrainFixtures() { ValidateFilmGrainFixture( TestImages.Heif.Av1FilmGrain8BitPayload, TestImages.Heif.Av1FilmGrain8BitReference, 100, 60, Av1BitDepth.EightBit, Av1ColorFormat.Yuv420); ValidateFilmGrainFixture( TestImages.Heif.Av1FilmGrain10BitPayload, TestImages.Heif.Av1FilmGrain10BitReference, 100, 60, Av1BitDepth.TenBit, Av1ColorFormat.Yuv422); ValidateFilmGrainFixture( TestImages.Heif.Av1FilmGrain12BitPayload, TestImages.Heif.Av1FilmGrain12BitReference, 100, 60, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv444); ValidateFilmGrainFixture( TestImages.Heif.Av1FilmGrain8BitRestrictedPayload, TestImages.Heif.Av1FilmGrain8BitRestrictedReference, 100, 60, Av1BitDepth.EightBit, Av1ColorFormat.Yuv420, requireRestrictedRange: true); ValidateFilmGrainFixture( TestImages.Heif.Av1FilmGrain8BitMonochromePayload, TestImages.Heif.Av1FilmGrain8BitMonochromeReference, 100, 60, Av1BitDepth.EightBit, Av1ColorFormat.Yuv400, requireRestrictedRange: true); ValidateFilmGrainFixture( TestImages.Heif.Av1FilmGrain12BitIdentityPayload, TestImages.Heif.Av1FilmGrain12BitIdentityReference, 100, 60, Av1BitDepth.TwelveBit, Av1ColorFormat.Yuv444, requireRestrictedRange: true, requireIdentityMatrix: true); ValidateFilmGrainFixture( TestImages.Heif.Av1FilmGrainOddDimensionsPayload, TestImages.Heif.Av1FilmGrainOddDimensionsReference, 33, 11, Av1BitDepth.EightBit, Av1ColorFormat.Yuv420); } /// /// Validates one elementary-stream sample and its containing AVIF image. /// /// The complete AVIF container. /// The AV1 elementary-stream sample extracted from the container. /// The native planar output produced by the pinned libaom decoder. /// The expected displayed width. /// The expected displayed height. /// The expected AV1 sample precision. /// The expected native chroma-sampling layout. /// The expected public HEIF sample precision. private static void ValidateFixture( string imagePath, string payloadPath, string referencePath, int width, int height, Av1BitDepth bitDepth, Av1ColorFormat colorFormat, HeifBitDepth metadataBitDepth) { ValidateNativeFixture(payloadPath, referencePath, width, height, bitDepth, colorFormat, false); ValidatePresentedImage(imagePath, width, height, metadataBitDepth); } /// /// Validates complete native-plane reconstruction for one AV1 elementary-stream sample. /// /// The AV1 elementary-stream sample. /// The native planar output produced by the pinned libaom decoder. /// The expected reconstructed width. /// The expected reconstructed height. /// The expected AV1 sample precision. /// The expected native chroma-sampling layout. /// Indicates whether the stream must signal and select nonzero CDEF strengths. /// Indicates whether the stream must use normative horizontal upscaling. /// Indicates whether the stream must select at least one loop-restoration unit. /// Indicates whether the displayed frame must synthesize signaled film grain. /// Indicates whether film grain must clip every plane to its restricted range. /// Indicates whether restricted chroma clipping must use the luma endpoints. /// Indicates whether the stream must signal a nonzero deblocking strength. /// Indicates whether the stream must select palette prediction for luma and chroma. /// A bit mask containing every selected loop-restoration filter type. private static int ValidateNativeFixture( string payloadPath, string referencePath, int width, int height, Av1BitDepth bitDepth, Av1ColorFormat colorFormat, bool requireActiveCdef, bool requireSuperResolution = false, bool requireLoopRestoration = false, bool requireFilmGrain = false, bool requireRestrictedRange = false, bool requireIdentityMatrix = false, bool requireActiveLoopFilter = true, bool requirePalette = false) { int restorationCoverage = 0; byte[] payload = TestFile.Create(payloadPath).Bytes; byte[] reference = TestFile.Create(referencePath).Bytes; using Av1Decoder decoder = new(Configuration.Default); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _); Assert.Equal(width, frameBuffer.Width); Assert.Equal(height, frameBuffer.Height); Assert.Equal(bitDepth, frameBuffer.BitDepth); Assert.Equal(colorFormat, frameBuffer.ColorFormat); Assert.NotNull(decoder.FrameHeader); if (requireSuperResolution) { ObuFrameSize frameSize = decoder.FrameHeader.FrameSize; Assert.True(frameSize.FrameWidth < frameSize.SuperResolutionUpscaledWidth); Assert.Equal(width, frameSize.SuperResolutionUpscaledWidth); if (!requireLoopRestoration) { // The original super-resolution fixtures isolate upscaling by disabling restoration. Assert.False(decoder.FrameHeader.LoopRestorationParameters.UsesLoopRestoration); } } if (requireActiveLoopFilter) { ObuLoopFilterParameters filterParameters = decoder.FrameHeader.LoopFilterParameters; Assert.True( filterParameters.FilterLevel[0] != 0 || filterParameters.FilterLevel[1] != 0 || filterParameters.FilterLevelU != 0 || filterParameters.FilterLevelV != 0); } if (requireActiveCdef) { Assert.NotNull(decoder.SequenceHeader); Assert.True(decoder.SequenceHeader.EnableCdef); Assert.False(decoder.FrameHeader.LoopRestorationParameters.UsesLoopRestoration); Assert.NotNull(decoder.FrameInfo); ObuConstraintDirectionalEnhancementFilterParameters parameters = decoder.FrameHeader.CdefParameters; bool hasActiveStrength = false; int superblockSizeLog2 = decoder.SequenceHeader.SuperblockSizeLog2; int superblockColumnCount = Av1Math.AlignPowerOf2(decoder.SequenceHeader.MaxFrameWidth, superblockSizeLog2) >> superblockSizeLog2; int superblockRowCount = Av1Math.AlignPowerOf2(decoder.SequenceHeader.MaxFrameHeight, superblockSizeLog2) >> superblockSizeLog2; for (int superblockRow = 0; superblockRow < superblockRowCount && !hasActiveStrength; superblockRow++) { for (int superblockColumn = 0; superblockColumn < superblockColumnCount && !hasActiveStrength; superblockColumn++) { Span selectedStrengths = decoder.FrameInfo.GetCdefStrength(new Point(superblockColumn, superblockRow)); // Unassigned entries belong to completely skipped units. Every assigned index must resolve through // the signaled table before the exact output can establish that CDEF changed reconstructed samples. foreach (int selectedStrength in selectedStrengths) { if (selectedStrength >= 0 && (parameters.YStrength[selectedStrength] != 0 || parameters.UvStrength[selectedStrength] != 0)) { hasActiveStrength = true; break; } } } } // The independent output only proves CDEF when the encoded frame selects at least one nonzero strength. Assert.True(hasActiveStrength); } if (requireLoopRestoration) { Assert.True(decoder.FrameHeader.LoopRestorationParameters.UsesLoopRestoration); Assert.NotNull(decoder.FrameInfo); restorationCoverage = GetRestorationCoverage(decoder); Assert.NotEqual(0, restorationCoverage); } if (requireFilmGrain) { Assert.True(decoder.FrameHeader.FilmGrainParameters.ApplyGrain); } if (requireRestrictedRange) { Assert.True(decoder.FrameHeader.FilmGrainParameters.ClipToRestrictedRange); } if (requireIdentityMatrix) { Assert.NotNull(decoder.SequenceHeader); Assert.Equal(ObuMatrixCoefficients.Identity, decoder.SequenceHeader.ColorConfig.MatrixCoefficients); } if (requirePalette) { Assert.Equal(RequiredPaletteCoverage, GetPaletteCoverage(decoder)); } AssertNativePlanesEqual(decoder, frameBuffer, reference); return restorationCoverage; } /// /// Validates lossless frame syntax and complete native reconstruction for one AVIF image. /// /// The independently encoded AVIF container. /// The raw planar output produced by the pinned scalar libaom decoder. /// The expected AV1 sample precision. private static void ValidateLosslessFixture(string imagePath, string referencePath, Av1BitDepth bitDepth) { byte[] imageBytes = TestFile.Create(imagePath).Bytes; byte[] referenceBytes = TestFile.Create(referencePath).Bytes; Span payload = GetSoleAv1ItemPayload(imageBytes); ReadOnlySpan nativeReference = referenceBytes; using Av1Decoder decoder = new(Configuration.Default); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _); Assert.Equal(LosslessFixtureWidth, frameBuffer.Width); Assert.Equal(LosslessFixtureHeight, frameBuffer.Height); Assert.Equal(bitDepth, frameBuffer.BitDepth); Assert.Equal(Av1ColorFormat.Yuv444, frameBuffer.ColorFormat); Assert.NotNull(decoder.SequenceHeader); Assert.NotNull(decoder.FrameHeader); Assert.NotNull(decoder.FrameInfo); Assert.True(decoder.FrameHeader.CodedLossless); Assert.True(decoder.FrameHeader.AllLossless); Assert.Equal(0, decoder.FrameHeader.QuantizationParameters.BaseQIndex); Assert.Equal(ObuMatrixCoefficients.Identity, decoder.SequenceHeader.ColorConfig.MatrixCoefficients); Assert.False(decoder.FrameHeader.AllowIntraBlockCopy); Assert.Equal(0, GetPaletteCoverage(decoder)); bool hasCodedResidual = false; int superblockSizeLog2 = decoder.SequenceHeader.SuperblockSizeLog2; int superblockColumnCount = Av1Math.AlignPowerOf2(decoder.SequenceHeader.MaxFrameWidth, superblockSizeLog2) >> superblockSizeLog2; int superblockRowCount = Av1Math.AlignPowerOf2(decoder.SequenceHeader.MaxFrameHeight, superblockSizeLog2) >> superblockSizeLog2; ReadOnlySpan planes = [Av1Plane.Y, Av1Plane.U, Av1Plane.V]; for (int superblockRow = 0; superblockRow < superblockRowCount && !hasCodedResidual; superblockRow++) { for (int superblockColumn = 0; superblockColumn < superblockColumnCount && !hasCodedResidual; superblockColumn++) { Point superblock = new(superblockColumn, superblockRow); foreach (Av1Plane plane in planes) { Span coefficients = plane switch { Av1Plane.Y => decoder.FrameInfo.GetCoefficientsY(superblock), Av1Plane.U => decoder.FrameInfo.GetCoefficientsU(superblock), _ => decoder.FrameInfo.GetCoefficientsV(superblock) }; // Each transform reserves an end index followed by its coefficients. Any nonzero stored value // proves that exact output traversed coefficient decoding, inverse quantization, and lossless WHT. foreach (int coefficient in coefficients) { if (coefficient != 0) { hasCodedResidual = true; break; } } if (hasCodedResidual) { break; } } } } Assert.True(hasCodedResidual); AssertNativePlanesEqual(decoder, frameBuffer, nativeReference); } /// /// Validates one independently encoded stream that activates constrained directional enhancement filtering. /// /// The AV1 elementary-stream sample. /// The native planar output produced by the pinned scalar libaom decoder. /// The expected reconstructed width. /// The expected reconstructed height. /// The expected AV1 sample precision. /// The expected native chroma-sampling layout. private static void ValidateActiveCdefFixture( string payloadPath, string referencePath, int width, int height, Av1BitDepth bitDepth, Av1ColorFormat colorFormat) => ValidateNativeFixture(payloadPath, referencePath, width, height, bitDepth, colorFormat, requireActiveCdef: true); /// /// Validates one independently encoded stream that activates normative super-resolution. /// /// The AV1 elementary-stream sample. /// The native planar output produced by the pinned scalar libaom decoder. /// The expected upscaled width. /// The expected reconstructed height. /// The expected AV1 sample precision. /// The expected native chroma-sampling layout. private static void ValidateSuperResolutionFixture( string payloadPath, string referencePath, int width, int height, Av1BitDepth bitDepth, Av1ColorFormat colorFormat) => ValidateNativeFixture( payloadPath, referencePath, width, height, bitDepth, colorFormat, requireActiveCdef: false, requireSuperResolution: true); /// /// Validates one independently encoded stream that activates normative loop restoration. /// /// The AV1 elementary-stream sample. /// The native planar output produced by the pinned scalar libaom decoder. /// The expected reconstructed width. /// The expected reconstructed height. /// The expected AV1 sample precision. /// The expected native chroma-sampling layout. /// Whether the stream must upscale from a narrower coded frame. /// A bit mask containing every selected loop-restoration filter type. private static int ValidateLoopRestorationFixture( string payloadPath, string referencePath, int width, int height, Av1BitDepth bitDepth, Av1ColorFormat colorFormat, bool requireSuperResolution = false) => ValidateNativeFixture( payloadPath, referencePath, width, height, bitDepth, colorFormat, requireActiveCdef: false, requireSuperResolution: requireSuperResolution, requireLoopRestoration: true); /// /// Validates one independently encoded stream that applies film grain to the displayed samples. /// /// The AV1 elementary-stream sample. /// The native planar output produced by the pinned scalar libaom decoder. /// The expected displayed width. /// The expected displayed height. /// The expected AV1 sample precision. /// The expected native chroma-sampling layout. /// Whether film grain must clip every plane to its restricted range. /// Whether restricted chroma clipping must use the luma endpoints. private static void ValidateFilmGrainFixture( string payloadPath, string referencePath, int width, int height, Av1BitDepth bitDepth, Av1ColorFormat colorFormat, bool requireRestrictedRange = false, bool requireIdentityMatrix = false) => ValidateNativeFixture( payloadPath, referencePath, width, height, bitDepth, colorFormat, requireActiveCdef: false, requireFilmGrain: true, requireRestrictedRange: requireRestrictedRange, requireIdentityMatrix: requireIdentityMatrix, requireActiveLoopFilter: false); /// /// Validates the public presentation and metadata produced from one complete AVIF container. /// /// The complete AVIF container. /// The expected displayed width. /// The expected displayed height. /// The expected public HEIF sample precision. private static void ValidatePresentedImage(string imagePath, int width, int height, HeifBitDepth metadataBitDepth) { DecoderOptions options = new() { MaxFrames = 1 }; byte[] imageBytes = TestFile.Create(imagePath).Bytes; using Image image = Image.Load(options, imageBytes); Assert.Equal(width, image.Width); Assert.Equal(height, image.Height); Assert.Single(image.Frames); HeifMetadata metadata = image.Metadata.GetHeifMetadata(); Assert.Equal(HeifCompressionMethod.Av1, metadata.CompressionMethod); Assert.Equal(metadataBitDepth, metadata.BitDepth); } /// /// Validates the exact public presentation of one independently encoded AVIF image against pinned scalar-libavif output. /// /// The complete AVIF container. /// The eight-bit RGBA output produced by the pinned scalar libavif decoder. /// The expected displayed width. /// The expected displayed height. /// The expected public HEIF sample precision. /// Whether the AV1 item must upscale from a narrower coded frame. /// Whether the AV1 item must select palette prediction for luma and chroma. /// Whether the AV1 item must select intra-block-copy prediction. private static void ValidatePresentedFixture( string imagePath, string referencePath, int width, int height, HeifBitDepth metadataBitDepth, bool requireSuperResolution = false, bool requirePalette = false, bool requireIntraBlockCopy = false) { DecoderOptions options = new() { MaxFrames = 1 }; byte[] imageBytes = TestFile.Create(imagePath).Bytes; byte[] referenceBytes = TestFile.Create(referencePath).Bytes; if (requireSuperResolution) { AssertUsesSuperResolution(imageBytes); } if (requirePalette) { AssertUsesPalette(imageBytes); } if (requireIntraBlockCopy) { AssertUsesIntraBlockCopy(imageBytes); } using Image image = Image.Load(options, imageBytes); using Image reference = Image.Load(referenceBytes); Assert.Equal(width, image.Width); Assert.Equal(height, image.Height); Assert.Single(image.Frames); HeifMetadata metadata = image.Metadata.GetHeifMetadata(); Assert.Equal(HeifCompressionMethod.Av1, metadata.CompressionMethod); Assert.Equal(metadataBitDepth, metadata.BitDepth); ImageComparer.Exact.VerifySimilarity(reference, image); } /// /// Verifies that the sole AV1 image item in an independently packaged AVIF uses normative super-resolution. /// /// The complete AVIF file. private static void AssertUsesSuperResolution(Span imageBytes) { Span payload = GetSoleAv1ItemPayload(imageBytes); using Av1Decoder decoder = new(Configuration.Default); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _); Assert.NotNull(decoder.FrameHeader); ObuFrameSize frameSize = decoder.FrameHeader.FrameSize; Assert.True(frameSize.FrameWidth < frameSize.SuperResolutionUpscaledWidth); Assert.Equal(frameBuffer.Width, frameSize.SuperResolutionUpscaledWidth); } /// /// Verifies that the sole AV1 image item in an independently encoded AVIF selects luma and chroma palettes. /// /// The complete AVIF file. private static void AssertUsesPalette(Span imageBytes) { Span payload = GetSoleAv1ItemPayload(imageBytes); using Av1Decoder decoder = new(Configuration.Default); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _); Assert.Equal(RequiredPaletteCoverage, GetPaletteCoverage(decoder)); } /// /// Verifies that the sole AV1 image item in an independently encoded AVIF selects intra-block-copy prediction. /// /// The complete AVIF file. private static void AssertUsesIntraBlockCopy(Span imageBytes) { Span payload = GetSoleAv1ItemPayload(imageBytes); using Av1Decoder decoder = new(Configuration.Default); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _); Assert.NotNull(decoder.FrameHeader); Assert.True(decoder.FrameHeader.AllowIntraBlockCopy); Assert.NotEqual(0, GetIntraBlockCopyBlockCount(decoder)); } /// /// Decodes the sole image item in an independently generated AVIF fixture and returns its restoration coverage. /// /// The complete AVIF file. /// A bit mask containing every selected loop-restoration filter type. private static int GetRestorationCoverageFromAvif(Span imageBytes) { Span payload = GetSoleAv1ItemPayload(imageBytes); using Av1Decoder decoder = new(Configuration.Default); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _); Assert.NotNull(decoder.FrameHeader); Assert.True(decoder.FrameHeader.LoopRestorationParameters.UsesLoopRestoration); Assert.NotNull(decoder.FrameInfo); int restorationCoverage = GetRestorationCoverage(decoder); Assert.NotEqual(0, restorationCoverage); return restorationCoverage; } /// /// Gets the partition types selected by one independently encoded AV1 elementary stream. /// /// The AV1 elementary-stream sample. /// A bit mask containing every selected partition type. private static int GetPartitionCoverage(string payloadPath) { byte[] payload = TestFile.Create(payloadPath).Bytes; using Av1Decoder decoder = new(Configuration.Default); using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _); Assert.NotNull(decoder.SequenceHeader); Assert.NotNull(decoder.FrameInfo); int superblockSizeLog2 = decoder.SequenceHeader.SuperblockSizeLog2; int superblockColumnCount = Av1Math.AlignPowerOf2(decoder.SequenceHeader.MaxFrameWidth, superblockSizeLog2) >> superblockSizeLog2; int superblockRowCount = Av1Math.AlignPowerOf2(decoder.SequenceHeader.MaxFrameHeight, superblockSizeLog2) >> superblockSizeLog2; int halfSuperblockSize = 1 << (superblockSizeLog2 - 1); int coverage = 0; // Mode records retain their bitstream traversal order and store each final coding block once, so iterating // the parsed count observes every selected leaf partition without repeatedly visiting its covered 4x4 cells. // Split itself creates no mode record. All other partition types are terminal, so a leaf below half the // superblock size on both axes proves that the parser reached it through at least one recursive split. for (int superblockRow = 0; superblockRow < superblockRowCount; superblockRow++) { for (int superblockColumn = 0; superblockColumn < superblockColumnCount; superblockColumn++) { Av1SuperblockInfo superblock = decoder.FrameInfo.GetSuperblock(new Point(superblockColumn, superblockRow)); foreach (Av1BlockModeInfo modeInfo in superblock.GetModeInfos()) { coverage |= 1 << (int)modeInfo.PartitionType; if (modeInfo.BlockSize.GetWidth() < halfSuperblockSize && modeInfo.BlockSize.GetHeight() < halfSuperblockSize) { coverage |= 1 << (int)Av1PartitionType.Split; } } } } return coverage; } /// /// Gets the complete media-data payload from a single-item AVIF conformance fixture. /// /// The complete AVIF file. /// The sole AV1 image-item payload. private static Span GetSoleAv1ItemPayload(Span imageBytes) { int offset = 0; while (offset < imageBytes.Length) { int headerLength = HeifBoxReader.ParseHeader(imageBytes[offset..], out long payloadLength, out Heif4CharCode boxType); Assert.InRange(payloadLength, 0, int.MaxValue); int payloadLength32 = (int)payloadLength; if (boxType == Heif4CharCode.Mdat) { // Every conformance container passed here deliberately stores its sole AV1 item as the complete // mdat payload, so feature assertions inspect the exact bytes used by public presentation decoding. return imageBytes.Slice(offset + headerLength, payloadLength32); } offset = checked(offset + headerLength + payloadLength32); } Assert.Fail("The AVIF fixture does not contain a media-data box."); return []; } /// /// Returns the luma and chroma palette classes selected by a decoded frame. /// /// The decoder after tile parsing and reconstruction. /// A bit mask containing the selected plane classes. private static int GetPaletteCoverage(Av1Decoder decoder) { Assert.NotNull(decoder.FrameHeader); Assert.NotNull(decoder.FrameInfo); int modeInfoWidth = Av1Math.DivideLog2Ceiling(decoder.FrameHeader.FrameSize.FrameWidth, Av1Constants.ModeInfoSizeLog2); int modeInfoHeight = Av1Math.DivideLog2Ceiling(decoder.FrameHeader.FrameSize.FrameHeight, Av1Constants.ModeInfoSizeLog2); int paletteCoverage = 0; for (int y = 0; y < modeInfoHeight; y++) { for (int x = 0; x < modeInfoWidth; x++) { Av1BlockModeInfo modeInfo = decoder.FrameInfo.GetModeInfoAt(new Point(x, y)); if (modeInfo.GetPaletteSize(Av1PlaneType.Y) != 0) { paletteCoverage |= LumaPaletteCoverage; } if (modeInfo.GetPaletteSize(Av1PlaneType.Uv) != 0) { paletteCoverage |= ChromaPaletteCoverage; } } } return paletteCoverage; } /// /// Counts the final coding blocks that select intra-block-copy prediction. /// /// The decoder after tile parsing and reconstruction. /// The number of selected intra-block-copy coding blocks. private static int GetIntraBlockCopyBlockCount(Av1Decoder decoder) { Assert.NotNull(decoder.SequenceHeader); Assert.NotNull(decoder.FrameInfo); int superblockSizeLog2 = decoder.SequenceHeader.SuperblockSizeLog2; int superblockColumnCount = Av1Math.AlignPowerOf2(decoder.SequenceHeader.MaxFrameWidth, superblockSizeLog2) >> superblockSizeLog2; int superblockRowCount = Av1Math.AlignPowerOf2(decoder.SequenceHeader.MaxFrameHeight, superblockSizeLog2) >> superblockSizeLog2; int blockCount = 0; // Mode records retain final coding blocks in bitstream order. Traversing each record once counts selected // intra-block-copy operations without repeatedly visiting the 4x4 cells covered by a larger block. for (int superblockRow = 0; superblockRow < superblockRowCount; superblockRow++) { for (int superblockColumn = 0; superblockColumn < superblockColumnCount; superblockColumn++) { Av1SuperblockInfo superblock = decoder.FrameInfo.GetSuperblock(new Point(superblockColumn, superblockRow)); foreach (Av1BlockModeInfo modeInfo in superblock.GetModeInfos()) { if (modeInfo.UseIntraBlockCopy) { blockCount++; } } } } return blockCount; } /// /// Returns the restoration algorithms selected by the decoded frame's unit grids. /// /// The decoder after tile parsing and reconstruction. /// A bit mask containing every selected loop-restoration filter type. private static int GetRestorationCoverage(Av1Decoder decoder) { int restorationCoverage = 0; for (int plane = 0; plane < decoder.SequenceHeader!.ColorConfig.PlaneCount; plane++) { int rowCount = decoder.FrameInfo!.GetLoopRestorationUnitRowCount(plane); int columnCount = decoder.FrameInfo.GetLoopRestorationUnitColumnCount(plane); for (int row = 0; row < rowCount; row++) { for (int column = 0; column < columnCount; column++) { Av1RestorationFilterType filterType = decoder.FrameInfo.GetLoopRestorationUnit(plane, row, column).FilterType; if (filterType != Av1RestorationFilterType.None) { restorationCoverage |= 1 << (int)filterType; } } } } return restorationCoverage; } /// /// Compares every visible native component sample with the independent planar reference. /// /// The decoder state used to identify the coded block containing a mismatch. /// The reconstructed AV1 component planes. /// The planar Y, U, and V samples produced by the pinned libaom decoder. private static void AssertNativePlanesEqual(Av1Decoder decoder, Av1FrameBuffer frameBuffer, ReadOnlySpan reference) { (int chromaSubsamplingX, int chromaSubsamplingY) = frameBuffer.ColorFormat switch { Av1ColorFormat.Yuv420 => (1, 1), Av1ColorFormat.Yuv422 => (1, 0), _ => (0, 0) }; int referenceOffset = 0; ReadOnlySpan planes = frameBuffer.ColorFormat == Av1ColorFormat.Yuv400 ? [Av1Plane.Y] : [Av1Plane.Y, Av1Plane.U, Av1Plane.V]; int mismatchCount = 0; Av1Plane largestMismatchPlane = default; int largestMismatchX = 0; int largestMismatchY = 0; ushort largestExpected = 0; ushort largestActual = 0; StringBuilder mismatchDescription = null; foreach (Av1Plane plane in planes) { int subsamplingX = plane == Av1Plane.Y ? 0 : chromaSubsamplingX; int subsamplingY = plane == Av1Plane.Y ? 0 : chromaSubsamplingY; int planeWidth = GetSubsampledSize(frameBuffer.Width, subsamplingX); int planeHeight = GetSubsampledSize(frameBuffer.Height, subsamplingY); if (frameBuffer.BitDepth == Av1BitDepth.EightBit) { Buffer2DRegion actualPlane = frameBuffer.DeriveBlockPointer(plane, subsamplingX, subsamplingY); for (int y = 0; y < planeHeight; y++) { Span actualRow = actualPlane.DangerousGetRowSpan(y)[..planeWidth]; ReadOnlySpan expectedRow = reference.Slice(referenceOffset, planeWidth); for (int x = 0; x < planeWidth; x++) { if (expectedRow[x] != actualRow[x]) { if (mismatchCount < 16) { mismatchDescription ??= new StringBuilder(); mismatchDescription.Append($" {plane}({x},{y})={expectedRow[x]}/{actualRow[x]}"); } if (mismatchCount == 0 || Math.Abs(expectedRow[x] - actualRow[x]) > Math.Abs(largestExpected - largestActual)) { largestMismatchPlane = plane; largestMismatchX = x; largestMismatchY = y; largestExpected = expectedRow[x]; largestActual = actualRow[x]; } mismatchCount++; } } referenceOffset += planeWidth; } } else { // aomdec writes high-bit-depth YUV as little-endian 16-bit values, independently of host endianness. for (int y = 0; y < planeHeight; y++) { Span actualRow = frameBuffer.GetHighBitDepthRowSpan(plane, y, subsamplingX, subsamplingY); for (int x = 0; x < planeWidth; x++) { ushort expected = BinaryPrimitives.ReadUInt16LittleEndian(reference.Slice(referenceOffset, sizeof(ushort))); if (expected != actualRow[x]) { if (mismatchCount < 16) { mismatchDescription ??= new StringBuilder(); mismatchDescription.Append($" {plane}({x},{y})={expected}/{actualRow[x]}"); } if (mismatchCount == 0 || Math.Abs(expected - actualRow[x]) > Math.Abs(largestExpected - largestActual)) { largestMismatchPlane = plane; largestMismatchX = x; largestMismatchY = y; largestExpected = expected; largestActual = actualRow[x]; } mismatchCount++; } referenceOffset += sizeof(ushort); } } } } Assert.Equal(reference.Length, referenceOffset); AssertSampleEqual( decoder, largestMismatchPlane, largestMismatchX, largestMismatchY, largestExpected, largestActual, mismatchCount, mismatchDescription?.ToString() ?? string.Empty); } /// /// Calculates a component dimension after chroma subsampling with the AV1 rounding rule. /// /// The luma dimension. /// The component subsampling shift. /// The subsampled component dimension. private static int GetSubsampledSize(int size, int subsampling) => (size + (1 << subsampling) - 1) >> subsampling; /// /// Reports the exact component coordinate when independently decoded samples differ. /// /// The decoder state used to identify the coded block containing the sample. /// The compared component plane. /// The sample X coordinate. /// The sample Y coordinate. /// The reference sample. /// The reconstructed sample. /// The total number of unequal native samples. /// The first unequal samples in plane traversal order. private static void AssertSampleEqual( Av1Decoder decoder, Av1Plane plane, int x, int y, ushort expected, ushort actual, int mismatchCount, string mismatchDescription) { if (expected != actual) { Av1FrameInfo frameInfo = Assert.IsType(decoder.FrameInfo); int modeInfoColumn = x >> Av1Constants.ModeInfoSizeLog2; int modeInfoRow = y >> Av1Constants.ModeInfoSizeLog2; Av1BlockModeInfo modeInfo = frameInfo.GetModeInfoAt(new Point(modeInfoColumn, modeInfoRow)); int blockColumn = modeInfoColumn; while (blockColumn > 0 && ReferenceEquals(frameInfo.GetModeInfoAt(new Point(blockColumn - 1, modeInfoRow)), modeInfo)) { blockColumn--; } int blockRow = modeInfoRow; while (blockRow > 0 && ReferenceEquals(frameInfo.GetModeInfoAt(new Point(modeInfoColumn, blockRow - 1)), modeInfo)) { blockRow--; } int superblockSize = frameInfo.SuperblockModeInfoSize; Av1SuperblockInfo superblock = frameInfo.GetSuperblock(new Point(blockColumn / superblockSize, blockRow / superblockSize)); Span transforms = superblock.GetTransformInfoY().Slice( modeInfo.GetFirstTransformLocation(Av1Plane.Y), modeInfo.GetTransformUnitCount(Av1Plane.Y)); Av1TransformInfo containingTransform = transforms[0]; int containingTransformIndex = 0; int transformColumn = modeInfoColumn - blockColumn; int transformRow = modeInfoRow - blockRow; for (int transformIndex = 0; transformIndex < transforms.Length; transformIndex++) { Av1TransformInfo transform = transforms[transformIndex]; if (transformColumn >= transform.OffsetX && transformColumn < transform.OffsetX + transform.Size.Get4x4WideCount() && transformRow >= transform.OffsetY && transformRow < transform.OffsetY + transform.Size.Get4x4HighCount()) { containingTransform = transform; containingTransformIndex = transformIndex; break; } } int superblockTransformIndex = modeInfo.GetFirstTransformLocation(Av1Plane.Y) + containingTransformIndex; Span superblockTransforms = superblock.GetTransformInfoY(); Span superblockCoefficients = superblock.CoefficientsY; int coefficientOffset = 0; for (int transformIndex = 0; transformIndex < superblockTransformIndex; transformIndex++) { if (superblockTransforms[transformIndex].CodeBlockFlag) { coefficientOffset += superblockCoefficients[coefficientOffset] + 1; } } StringBuilder coefficientDescription = new(); if (containingTransform.CodeBlockFlag) { int coefficientCount = superblockCoefficients[coefficientOffset]; coefficientDescription.Append($", quantized-coefficients={coefficientCount}:["); for (int coefficientIndex = 0; coefficientIndex < coefficientCount; coefficientIndex++) { if (coefficientIndex != 0) { coefficientDescription.Append(','); } coefficientDescription.Append(superblockCoefficients[coefficientOffset + coefficientIndex + 1]); } coefficientDescription.Append(']'); } ObuFrameHeader frameHeader = Assert.IsType(decoder.FrameHeader); int cdefUnitColumn = (modeInfoColumn % superblockSize) / CdefUnitModeInfoSize; int cdefUnitRow = (modeInfoRow % superblockSize) / CdefUnitModeInfoSize; int cdefStrengthIndex = frameInfo.GetCdefStrength(superblock.Position)[cdefUnitColumn + (cdefUnitRow << 1)]; int cdefStrength = cdefStrengthIndex < 0 ? -1 : frameHeader.CdefParameters.YStrength[cdefStrengthIndex]; int nextModeInfoRow = Math.Min(modeInfoRow + 1, frameHeader.ModeInfoRowCount - 1); Av1BlockModeInfo nextRowModeInfo = frameInfo.GetModeInfoAt(new Point(modeInfoColumn, nextModeInfoRow)); Av1BlockModeInfo aboveModeInfo = frameInfo.GetModeInfoAt(new Point(modeInfoColumn, Math.Max(blockRow - 1, 0))); Av1BlockModeInfo leftModeInfo = frameInfo.GetModeInfoAt(new Point(Math.Max(blockColumn - 1, 0), modeInfoRow)); // Exact conformance failures need the owning syntax state. A coordinate alone does not distinguish // prediction, residual reconstruction, and in-loop filtering failures inside a large coded frame. Assert.Fail( $"Plane {plane} differs at ({x}, {y}): expected {expected}, actual {actual}. " + $"Total unequal samples={mismatchCount}:{mismatchDescription}. " + $"Block={modeInfo.BlockSize}, mode={modeInfo.YMode}, partition={modeInfo.PartitionType}, skip={modeInfo.Skip}, " + $"filter-intra={modeInfo.UseFilterIntra}/{modeInfo.FilterIntraMode}, angle-delta={modeInfo.GetAngleDelta(plane)}, " + $"palette-size={modeInfo.GetPaletteSize(plane)}, transforms={modeInfo.GetTransformUnitCount(plane)}, " + $"transform={containingTransform.Size}/{containingTransform.Type}/coded={containingTransform.CodeBlockFlag} " + $"at ({containingTransform.OffsetX}, {containingTransform.OffsetY}), block-origin=({blockColumn}, {blockRow}). " + $"Loop-filter={frameHeader.LoopFilterParameters.FilterLevel[0]}/{frameHeader.LoopFilterParameters.FilterLevel[1]}, " + $"sharpness={frameHeader.LoopFilterParameters.SharpnessLevel}, delta-q={frameHeader.DeltaQParameters.IsPresent}, " + $"superblock-q={superblock.SuperblockQuantizerIndex}{coefficientDescription}, " + $"delta-lf={frameHeader.DeltaLoopFilterParameters.IsPresent}/{frameHeader.DeltaLoopFilterParameters.IsMulti}, " + $"CDEF={cdefStrengthIndex}/{cdefStrength}, restoration={frameHeader.LoopRestorationParameters.Items[0].Type}, " + $"film-grain={frameHeader.FilmGrainParameters.ApplyGrain}, " + $"tiles={frameHeader.TilesInfo.TileColumnCount}x{frameHeader.TilesInfo.TileRowCount}, " + $"first-tile-end=({frameHeader.TilesInfo.TileColumnStartModeInfo[1]}, {frameHeader.TilesInfo.TileRowStartModeInfo[1]}). " + $"Neighbors: above={aboveModeInfo.BlockSize}/{aboveModeInfo.YMode}/skip={aboveModeInfo.Skip}, " + $"left={leftModeInfo.BlockSize}/{leftModeInfo.YMode}/skip={leftModeInfo.Skip}, " + $"next-row={nextRowModeInfo.BlockSize}/{nextRowModeInfo.YMode}/skip={nextRowModeInfo.Skip}/" + $"angle-delta={nextRowModeInfo.GetAngleDelta(plane)}/transforms={nextRowModeInfo.GetTransformUnitCount(plane)}."); } } }