// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Formats.Heif.Av1; using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; /// /// Verifies the frame-level derivation of AV1 skip-mode reference pairs. /// [Trait("Format", "Avif")] public class ObuSkipModeParametersTests { /// /// The public theory-data representation of . /// private const int KeyFrameValue = (int)ObuFrameType.KeyFrame; /// /// The public theory-data representation of . /// private const int InterFrameValue = (int)ObuFrameType.InterFrame; /// /// The public theory-data representation of . /// private const int SingleReferenceValue = (int)ObuReferenceMode.SingleReference; /// /// The public theory-data representation of . /// private const int ReferenceModeSelectValue = (int)ObuReferenceMode.ReferenceModeSelect; /// /// Verifies signed order-hint distances across the modulo-domain boundary. /// [Fact] public void GetRelativeDistanceWrapsWithinConfiguredDomain() { ObuOrderHintInfo orderHintInfo = CreateOrderHintInfo(); Assert.Equal(-2, orderHintInfo.GetRelativeDistance(15, 1)); Assert.Equal(2, orderHintInfo.GetRelativeDistance(1, 15)); } /// /// Verifies that disabled order hints have no temporal ordering. /// [Fact] public void GetRelativeDistanceReturnsZeroWhenOrderHintsAreDisabled() { ObuOrderHintInfo orderHintInfo = new(); Assert.Equal(0, orderHintInfo.GetRelativeDistance(15, 1)); } /// /// Verifies that skip mode selects the nearest past and future canonical reference roles. /// [Fact] public void DeriveSelectsNearestForwardAndBackwardReferences() { ObuOrderHintInfo orderHintInfo = CreateOrderHintInfo(); ObuFrameHeader frameHeader = CreateInterFrame(8, [7, 3, 6, 2, 10, 12, 15]); frameHeader.SkipModeParameters.Derive(orderHintInfo, frameHeader); Assert.True(frameHeader.SkipModeParameters.SkipModeAllowed); Assert.Equal(Av1ReferenceFrameType.Last, frameHeader.SkipModeParameters.FirstReferenceFrame); Assert.Equal(Av1ReferenceFrameType.Backward, frameHeader.SkipModeParameters.SecondReferenceFrame); } /// /// Verifies that the derived pair identifies canonical roles rather than their physical reference-map slots. /// [Fact] public void DeriveOrdersCanonicalRolesIndependentlyOfMappedSlots() { ObuOrderHintInfo orderHintInfo = CreateOrderHintInfo(); ObuFrameHeader frameHeader = CreateInterFrame(8, [7, 3, 6, 2, 10, 12, 15]); Span referenceFrameIndices = frameHeader.GetReferenceFrameIndices(); Span referenceOrderHints = frameHeader.GetReferenceOrderHints(); // Several canonical roles deliberately share physical slot seven. The first matching role remains LAST, while // the future BWDREF role maps to slot four; neither physical slot number becomes part of the derived pair. referenceFrameIndices.Fill(7); referenceFrameIndices[(int)Av1ReferenceFrameType.Backward - 1] = 4; referenceOrderHints[7] = 7; frameHeader.SkipModeParameters.Derive(orderHintInfo, frameHeader); Assert.True(frameHeader.SkipModeParameters.SkipModeAllowed); Assert.Equal(Av1ReferenceFrameType.Last, frameHeader.SkipModeParameters.FirstReferenceFrame); Assert.Equal(Av1ReferenceFrameType.Backward, frameHeader.SkipModeParameters.SecondReferenceFrame); } /// /// Verifies that a frame with only future references cannot use skip mode. /// [Fact] public void DeriveDisallowsSkipModeWithoutForwardReference() { ObuOrderHintInfo orderHintInfo = CreateOrderHintInfo(); ObuFrameHeader frameHeader = CreateInterFrame(8, [9, 10, 11, 12, 13, 14, 15]); frameHeader.SkipModeParameters.Derive(orderHintInfo, frameHeader); Assert.False(frameHeader.SkipModeParameters.SkipModeAllowed); Assert.Equal(Av1ReferenceFrameType.None, frameHeader.SkipModeParameters.FirstReferenceFrame); Assert.Equal(Av1ReferenceFrameType.None, frameHeader.SkipModeParameters.SecondReferenceFrame); } /// /// Verifies that a forward-only frame selects the two closest distinct past reference orders. /// [Fact] public void SelectsTwoForwardReferencesWithoutBackwardReference() { ObuOrderHintInfo orderHintInfo = CreateOrderHintInfo(); ObuFrameHeader frameHeader = CreateInterFrame(8, [7, 3, 6, 2, 1, 5, 4]); frameHeader.SkipModeParameters.Derive(orderHintInfo, frameHeader); Assert.True(frameHeader.SkipModeParameters.SkipModeAllowed); Assert.Equal(Av1ReferenceFrameType.Last, frameHeader.SkipModeParameters.FirstReferenceFrame); Assert.Equal(Av1ReferenceFrameType.Last3, frameHeader.SkipModeParameters.SecondReferenceFrame); } /// /// Verifies that modulo wraparound participates in nearest-reference selection. /// [Fact] public void DeriveSelectsReferencesAcrossOrderHintWraparound() { ObuOrderHintInfo orderHintInfo = CreateOrderHintInfo(); ObuFrameHeader frameHeader = CreateInterFrame(1, [12, 15, 11, 10, 2, 5, 7]); frameHeader.SkipModeParameters.Derive(orderHintInfo, frameHeader); Assert.True(frameHeader.SkipModeParameters.SkipModeAllowed); Assert.Equal(Av1ReferenceFrameType.Last2, frameHeader.SkipModeParameters.FirstReferenceFrame); Assert.Equal(Av1ReferenceFrameType.Backward, frameHeader.SkipModeParameters.SecondReferenceFrame); } /// /// Verifies that skip mode remains unavailable without two temporally distinct usable reference orders. /// [Fact] public void DeriveDisallowsSkipModeWithoutReferencePair() { ObuOrderHintInfo orderHintInfo = CreateOrderHintInfo(); ObuFrameHeader frameHeader = CreateInterFrame(8, [7, 8, 8, 8, 8, 8, 8]); frameHeader.SkipModeParameters.Derive(orderHintInfo, frameHeader); Assert.False(frameHeader.SkipModeParameters.SkipModeAllowed); Assert.Equal(Av1ReferenceFrameType.None, frameHeader.SkipModeParameters.FirstReferenceFrame); Assert.Equal(Av1ReferenceFrameType.None, frameHeader.SkipModeParameters.SecondReferenceFrame); } /// /// Verifies that deriving an ineligible frame clears a reference pair retained by an earlier derivation. /// [Fact] public void DeriveClearsPreviousReferencePair() { ObuOrderHintInfo orderHintInfo = CreateOrderHintInfo(); ObuFrameHeader frameHeader = CreateInterFrame(8, [7, 3, 6, 2, 10, 12, 15]); frameHeader.SkipModeParameters.Derive(orderHintInfo, frameHeader); frameHeader.ReferenceMode = ObuReferenceMode.SingleReference; frameHeader.SkipModeParameters.Derive(orderHintInfo, frameHeader); Assert.False(frameHeader.SkipModeParameters.SkipModeAllowed); Assert.Equal(Av1ReferenceFrameType.None, frameHeader.SkipModeParameters.FirstReferenceFrame); Assert.Equal(Av1ReferenceFrameType.None, frameHeader.SkipModeParameters.SecondReferenceFrame); } /// /// Verifies the frame modes for which the AV1 syntax forbids skip-mode signaling. /// /// Whether the sequence enables order hints. /// The numeric coded-frame-type value. /// The numeric frame-level reference-mode value. [Theory] [InlineData(false, InterFrameValue, ReferenceModeSelectValue)] [InlineData(true, KeyFrameValue, ReferenceModeSelectValue)] [InlineData(true, InterFrameValue, SingleReferenceValue)] public void DeriveDisallowsSkipModeForIneligibleFrameSyntax( bool enableOrderHint, int frameTypeValue, int referenceModeValue) { ObuOrderHintInfo orderHintInfo = CreateOrderHintInfo(); orderHintInfo.EnableOrderHint = enableOrderHint; ObuFrameHeader frameHeader = CreateInterFrame(8, [7, 3, 6, 2, 10, 12, 15]); frameHeader.FrameType = (ObuFrameType)frameTypeValue; frameHeader.ReferenceMode = (ObuReferenceMode)referenceModeValue; frameHeader.SkipModeParameters.Derive(orderHintInfo, frameHeader); Assert.False(frameHeader.SkipModeParameters.SkipModeAllowed); Assert.Equal(Av1ReferenceFrameType.None, frameHeader.SkipModeParameters.FirstReferenceFrame); Assert.Equal(Av1ReferenceFrameType.None, frameHeader.SkipModeParameters.SecondReferenceFrame); } /// /// Creates the four-bit modulo order-hint configuration used by the derivation scenarios. /// /// The enabled order-hint configuration. private static ObuOrderHintInfo CreateOrderHintInfo() => new() { EnableOrderHint = true, OrderHintBits = 4, }; /// /// Creates an inter frame whose seven canonical roles map directly to slots zero through six. /// /// The current frame order hint. /// The order hint selected by each canonical role. /// The initialized inter-frame header. private static ObuFrameHeader CreateInterFrame(uint currentOrderHint, ReadOnlySpan referenceOrderHints) { ObuFrameHeader frameHeader = new() { FrameType = ObuFrameType.InterFrame, OrderHint = currentOrderHint, ReferenceMode = ObuReferenceMode.ReferenceModeSelect, }; Span referenceFrameIndices = frameHeader.GetReferenceFrameIndices(); Span referenceMapOrderHints = frameHeader.GetReferenceOrderHints(); for (int referenceIndex = 0; referenceIndex < Av1Constants.ReferencesPerFrame; referenceIndex++) { referenceFrameIndices[referenceIndex] = (uint)referenceIndex; referenceMapOrderHints[referenceIndex] = referenceOrderHints[referenceIndex]; } return frameHeader; } }