diff --git a/HEIF_IMPLEMENTATION_PLAN.md b/HEIF_IMPLEMENTATION_PLAN.md index e76f30d52c..40522b3f34 100644 --- a/HEIF_IMPLEMENTATION_PLAN.md +++ b/HEIF_IMPLEMENTATION_PLAN.md @@ -821,6 +821,7 @@ Encoder verification contract: - [~] Current-libaom `av1_quantize_fp_no_qmatrix` arithmetic is implemented as a closed generic forward-quantizer family with Vector512, Vector256, Vector128, and scalar paths, raster-order output, coded 64-point coefficient limits, and scan-order EOB selection. Transform search, coefficient optimization, and lossless behavior remain. - [ ] Implement real rate-distortion selection and make quality and effort change work, size, and output quality. - [~] The tile writer now publishes one packed coefficient context per covered 4x4 edge unit and derives luma/chroma skip plus DC-sign contexts from the complete transform edges using current-libaom units. Complete tile traversal, initialized picture state, and verified CDF update behavior remain. +- [~] Encoder mode information now uses a frame-owned reference grid over its contiguous allocation, matching current libaom's `mi_grid_base` and `mi_alloc` ownership without per-block tail copies. Signed relative neighbor lookup, 4x4-unit addressing, and mutable selected skip syntax have focused contracts; complete mode decision still remains. - [ ] Implement legal deblocking, CDEF, restoration, super-resolution, and film-grain signaling decisions. - [~] The coefficient symbol encoder now reuses tile-lifetime level and context workspaces instead of allocating per transform. Every remaining encoder fragment must be audited before it becomes active. - [~] The planar conversion, forward transform, and forward quantizer use descending SIMD dispatch: Vector512, Vector256, Vector128, then scalar. Apply the same rule to every later hot-path family. diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs index 5c9702f01f..d70470fd0b 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs @@ -11,44 +11,34 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; internal class Av1EncoderBlockModeInfo { /// - /// Gets the encoded block size. + /// Gets or sets the encoded block size. /// - public Av1BlockSize BlockSize { get; } + public Av1BlockSize BlockSize { get; set; } /// - /// Gets the selected luma prediction mode. + /// Gets or sets the partition type that produced the block. /// - public Av1PredictionMode PredictionMode { get; } + public Av1PartitionType PartitionType { get; set; } /// - /// Gets the partition type that produced the block. + /// Gets or sets a value indicating whether residual coefficients are omitted for the block. /// - public Av1PartitionType PartitionType { get; } + public bool Skip { get; set; } /// - /// Gets the selected chroma prediction mode. + /// Gets or sets a value indicating whether compound skip mode is selected. /// - public Av1ChromaPredictionMode UvPredictionMode { get; } + public bool SkipMode { get; set; } /// - /// Gets a value indicating whether residual coefficients are omitted for the block. + /// Gets or sets a value indicating whether intra block copy is selected. /// - public bool Skip { get; } = true; + public bool UseIntraBlockCopy { get; set; } /// - /// Gets a value indicating whether compound skip mode is selected. + /// Gets or sets the segmentation identifier assigned to the block. /// - public bool SkipMode { get; } = true; - - /// - /// Gets a value indicating whether intra block copy is selected. - /// - public bool UseIntraBlockCopy { get; } = true; - - /// - /// Gets the segmentation identifier assigned to the block. - /// - public int SegmentId { get; } + public int SegmentId { get; set; } /// /// Gets or sets the transform-tree depth selected for the block. diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs index 861857a2f5..c4517af43b 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs @@ -9,23 +9,14 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; internal class Av1MacroBlockD { /// - /// Stores the mode-information entries exposed through . + /// Stores the frame's mode-information reference grid. /// - private Av1ModeInfo[] modeInfo = []; + private Av1ModeInfo[] modeInfoGrid = []; /// - /// Gets or sets the mode-information entries for the current block and its mapped neighbors. + /// Stores the current block's linear position in . /// - public required ReadOnlySpan ModeInfo - { - get => this.modeInfo; - set - { - // A span cannot be retained by the class, so preserve the selected map entries in owned storage. - this.modeInfo = new Av1ModeInfo[value.Length]; - value.CopyTo(this.modeInfo); - } - } + private int modeInfoIndex; /// /// Gets or sets the tile containing the current block. @@ -86,4 +77,22 @@ internal class Av1MacroBlockD /// Gets or sets a value indicating whether this block is the second half of a rectangular partition. /// public bool IsSecondRectangle { get; set; } + + /// + /// Selects the current entry in the frame-owned mode-information reference grid. + /// + /// The frame-owned mode-information reference grid. + /// The current block's linear grid index. + public void SetModeInfoGrid(Av1ModeInfo[] grid, int index) + { + this.modeInfoGrid = grid; + this.modeInfoIndex = index; + } + + /// + /// Gets a mode-information entry relative to the current block. + /// + /// The signed linear offset from the current block. + /// The mapped neighboring or current entry. + public Av1ModeInfo GetRelativeModeInfo(int offset) => this.modeInfoGrid[this.modeInfoIndex + offset]; } diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs index 99a566b2c5..f8e9c13989 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockModeInfo.cs @@ -13,11 +13,6 @@ internal class Av1MacroBlockModeInfo /// public required Av1EncoderBlockModeInfo Block { get; set; } - /// - /// Gets or sets the luma palette decisions for the block. - /// - public required Av1PaletteLumaModeInfo Palette { get; set; } - /// /// Gets or sets the constrained directional enhancement filter strength for the block. /// diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PaletteLumaModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PaletteLumaModeInfo.cs deleted file mode 100644 index 67e9e1b81e..0000000000 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PaletteLumaModeInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; - -/// -/// Reserves encoder-side state for AV1 luma palette mode decisions. -/// -internal class Av1PaletteLumaModeInfo -{ -} diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs index f37cdbc757..b32eeab6d3 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; - namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; /// @@ -51,14 +49,14 @@ internal class Av1PictureControlSet public required byte[] SegmentationNeighborMap { get; set; } /// - /// Gets the frame grid that maps each 4x4 position to its mode-information span. + /// Gets or sets the frame grid that maps each 4x4 position to its mode-information entry. /// - public Av1ModeInfo[][] ModeInfoGrid { get; } = []; + public required Av1ModeInfo[] ModeInfoGrid { get; set; } /// /// Gets or sets the contiguous mode-information storage addressed by . /// - public required Av1ModeInfo[] Mip { get; set; } + public required Av1ModeInfo[] ModeInfoAllocation { get; set; } /// /// Gets or sets the row stride of in 4x4 mode-information units. @@ -76,51 +74,29 @@ internal class Av1PictureControlSet public required int[][] CdefPreset { get; set; } /// - /// Gets the mode-information span mapped to a frame position. + /// Gets the mode-information entry mapped to a frame position. /// /// The frame position in 4x4 mode-information units. - /// The mode-information span beginning at the position. - public Span GetFromModeInfoGrid(Point position) + /// The mapped mode-information entry. + public Av1ModeInfo GetFromModeInfoGrid(Point position) => this.ModeInfoGrid[(position.Y * this.ModeInfoStride) + position.X]; - /// - /// Maps a frame position to the supplied mode-information span. - /// - /// The frame position in 4x4 mode-information units. - /// The mode-information entries to map. - public void SetModeInfoGridRow(Point position, ReadOnlySpan span) - => this.SetModeInfoGridRow((position.Y * this.ModeInfoStride) + position.X, span); - - /// - /// Maps a linear grid offset to the supplied mode-information span. - /// - /// The linear grid offset. - /// The mode-information entries to map. - public void SetModeInfoGridRow(int offset, ReadOnlySpan span) - { - // Grid entries own their arrays because the source span can refer to temporary traversal state. - this.ModeInfoGrid[offset] = new Av1ModeInfo[span.Length]; - span.CopyTo(this.ModeInfoGrid[offset]); - } - /// /// Gets the macroblock mode information at a block origin and refreshes its grid mapping. /// - /// The block origin in 4x4 mode-information units. + /// The block position in 4x4 mode-information units. /// The macroblock mode information at the origin. - public Av1MacroBlockModeInfo GetMacroBlockModeInfo(Point blockOrigin) + public Av1MacroBlockModeInfo GetMacroBlockModeInfo(Point modeInfoPosition) { int modeInfoStride = this.ModeInfoStride; - int offset = (blockOrigin.Y * modeInfoStride) + blockOrigin.X; + int offset = (modeInfoPosition.Y * modeInfoStride) + modeInfoPosition.X; - // Rectangular mode-decision blocks can replace grid entries. Restore the entry from the - // contiguous backing store, whose index is halved when 4x4 blocks are globally disabled. + // The grid stores references into the contiguous mode-info allocation, matching libaom's + // mi_grid_base/mi_alloc ownership without copying a tail for every coded block. int disallow4x4 = this.Disallow4x4AllFrames ? 1 : 0; - int mipOffset = ((blockOrigin.Y >> disallow4x4) * (modeInfoStride >> disallow4x4)) + (blockOrigin.X >> disallow4x4); - this.SetModeInfoGridRow(offset, ((Span)this.Mip)[mipOffset..]); - - // The first mapped entry owns the macroblock state for the entire block. - Av1ModeInfo modeInfo = this.ModeInfoGrid[offset][0]; + int allocationOffset = ((modeInfoPosition.Y >> disallow4x4) * (modeInfoStride >> disallow4x4)) + (modeInfoPosition.X >> disallow4x4); + Av1ModeInfo modeInfo = this.ModeInfoAllocation[allocationOffset]; + this.ModeInfoGrid[offset] = modeInfo; return modeInfo.MacroBlockModeInfo; } diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs index 7ea9ca7278..7e1e5fdae1 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs @@ -364,26 +364,26 @@ internal partial class Av1TileWriter Av1BlockGeometry blockGeometry = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blk_ptr.ModeDecisionScanIndex); Point blockOrigin = Point.Add(entropyCodingContext.SuperblockOrigin, (Size)blockGeometry.Origin); Av1BlockSize blockSize = blockGeometry.BlockSize; - Av1MacroBlockModeInfo macroBlockModeInfo = pcs.GetMacroBlockModeInfo(blockOrigin); + int mi_row = blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2; + int mi_col = blockOrigin.X >> Av1Constants.ModeInfoSizeLog2; + int mi_stride = pcs.Parent.Common.ModeInfoStride; + int offset = (mi_row * mi_stride) + mi_col; + Point modeInfoPosition = new(mi_col, mi_row); + Av1MacroBlockModeInfo macroBlockModeInfo = pcs.GetMacroBlockModeInfo(modeInfoPosition); bool skipWritingCoefficients = macroBlockModeInfo.Block.Skip; entropyCodingContext.MacroBlockModeInfo = macroBlockModeInfo; bool skip_mode = macroBlockModeInfo.Block.SkipMode; Guard.MustBeLessThan((int)blockSize, (int)Av1BlockSize.AllSizes, nameof(blockSize)); - int mi_row = blockOrigin.Y >> Av1Constants.ModeInfoSizeLog2; - int mi_col = blockOrigin.X >> Av1Constants.ModeInfoSizeLog2; - int mi_stride = pcs.Parent.Common.ModeInfoStride; - int offset = (mi_row * mi_stride) + mi_col; - Point modeInfoPosition = new(mi_col, mi_row); - blk_ptr.MacroBlock.ModeInfo = pcs.ModeInfoGrid[offset]; + blk_ptr.MacroBlock.SetModeInfoGrid(pcs.ModeInfoGrid, offset); blk_ptr.MacroBlock.Tile = new Av1TileInfo(tb_ptr.TileInfo); blk_ptr.MacroBlock.IsUpAvailable = modeInfoPosition.Y > tb_ptr.TileInfo.ModeInfoRowStart; blk_ptr.MacroBlock.IsLeftAvailable = modeInfoPosition.X > tb_ptr.TileInfo.ModeInfoColumnStart; if (blk_ptr.MacroBlock.IsUpAvailable) { - blk_ptr.MacroBlock.AboveMacroBlock = blk_ptr.MacroBlock.ModeInfo[-mi_stride].MacroBlockModeInfo; + blk_ptr.MacroBlock.AboveMacroBlock = blk_ptr.MacroBlock.GetRelativeModeInfo(-mi_stride).MacroBlockModeInfo; } else { @@ -392,7 +392,7 @@ internal partial class Av1TileWriter if (blk_ptr.MacroBlock.IsLeftAvailable) { - blk_ptr.MacroBlock.LeftMacroBlock = blk_ptr.MacroBlock.ModeInfo[-1].MacroBlockModeInfo; + blk_ptr.MacroBlock.LeftMacroBlock = blk_ptr.MacroBlock.GetRelativeModeInfo(-1).MacroBlockModeInfo; } else { @@ -600,12 +600,12 @@ internal partial class Av1TileWriter if (xd.IsLeftAvailable) { // Key-frame neighbors are intra blocks, so their luma modes directly select the context class. - intraLumaLeftMode = xd.ModeInfo[-1].MacroBlockModeInfo.Block.Mode; + intraLumaLeftMode = xd.GetRelativeModeInfo(-1).MacroBlockModeInfo.Block.Mode; } if (xd.IsUpAvailable) { - intraLumaTopMode = xd.ModeInfo[-xd.ModeInfoStride].MacroBlockModeInfo.Block.Mode; + intraLumaTopMode = xd.GetRelativeModeInfo(-xd.ModeInfoStride).MacroBlockModeInfo.Block.Mode; } above_ctx = IntraModeContextLookup[(int)intraLumaTopMode]; @@ -738,7 +738,8 @@ internal partial class Av1TileWriter Av1NeighborArrayUnit cr_dc_sign_level_coeff_na = pcs.CrDcSignLevelCoefficientNeighbors[tile_idx]; Av1NeighborArrayUnit cb_dc_sign_level_coeff_na = pcs.CbDcSignLevelCoefficientNeighbors[tile_idx]; Av1BlockGeometry blk_geom = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blk_ptr.ModeDecisionScanIndex); - Av1MacroBlockModeInfo mbmi = pcs.GetMacroBlockModeInfo(blockOrigin); + Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; + Av1MacroBlockModeInfo mbmi = pcs.GetMacroBlockModeInfo(modeInfoPosition); bool skip_coeff = mbmi.Block.Skip; // Store the block-size split mask across the edges that future partition symbols can observe. @@ -843,7 +844,7 @@ internal partial class Av1TileWriter // int m = ~((1 << (6 - Av1Constants.ModeInfoSizeLog2)) - 1); // cm->mi_grid_visible[(mi_row & m) * cm->mi_stride + (mi_col & m)]; - Av1ModeInfo mi = pcs.GetFromModeInfoGrid(modeInfoPosition)[0]; + Av1ModeInfo mi = pcs.GetFromModeInfoGrid(modeInfoPosition); // Each superblock begins with all contained 64x64 filter units unassigned. if ((modeInfoPosition.Y & (scs.SequenceHeader.SuperblockModeInfoSize - 1)) == 0 && @@ -897,11 +898,12 @@ internal partial class Av1TileWriter // Prediction cannot cross tile boundaries even when frame mode information exists there. macroBlock.IsUpAvailable = modeInfoPosition.Y > tile.ModeInfoRowStart; macroBlock.IsLeftAvailable = modeInfoPosition.X > tile.ModeInfoColumnStart; - macroBlock.ModeInfo = pcs.GetFromModeInfoGrid(modeInfoPosition); + int modeInfoIndex = (modeInfoPosition.Y * modeInfoStride) + modeInfoPosition.X; + macroBlock.SetModeInfoGrid(pcs.ModeInfoGrid, modeInfoIndex); if (macroBlock.IsUpAvailable) { - macroBlock.AboveMacroBlock = macroBlock.ModeInfo[-modeInfoStride].MacroBlockModeInfo; + macroBlock.AboveMacroBlock = macroBlock.GetRelativeModeInfo(-modeInfoStride).MacroBlockModeInfo; } else { @@ -910,7 +912,7 @@ internal partial class Av1TileWriter if (macroBlock.IsLeftAvailable) { - macroBlock.LeftMacroBlock = macroBlock.ModeInfo[-1].MacroBlockModeInfo; + macroBlock.LeftMacroBlock = macroBlock.GetRelativeModeInfo(-1).MacroBlockModeInfo; } else { diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs index d800483253..4270b303e4 100644 --- a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs @@ -4,6 +4,7 @@ using System.Buffers; using SixLabors.ImageSharp.Formats.Heif.Av1; using SixLabors.ImageSharp.Formats.Heif.Av1.Entropy; +using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; @@ -72,6 +73,113 @@ public class Av1CoefficientsEntropyTests Assert.Equal(expectedSkipContext, context.SkipContext); } + [Theory] + [InlineData(false, 2, 1, 6, 6)] + [InlineData(true, 2, 2, 10, 3)] + public void PictureControlSetMapsModeInfoAllocationByReference( + bool disallow4x4, + int column, + int row, + int gridOffset, + int allocationOffset) + { + Av1ModeInfo expected = CreateModeInfo(Av1PredictionMode.Paeth); + Av1ModeInfo[] allocation = new Av1ModeInfo[8]; + allocation[allocationOffset] = expected; + Av1PictureControlSet picture = new() + { + PartitionContexts = [], + LuminanceDcSignLevelCoefficientNeighbors = [], + CrDcSignLevelCoefficientNeighbors = [], + CbDcSignLevelCoefficientNeighbors = [], + TransformFunctionContexts = [], + Sequence = new Av1SequenceControlSet { SequenceHeader = new ObuSequenceHeader() }, + Parent = new Av1PictureParentControlSet + { + Common = new Av1EncoderCommon + { + FrameSize = new ObuFrameSize(), + TilesInfo = new ObuTileGroupHeader() + }, + FrameHeader = new ObuFrameHeader(), + PreviousQIndex = [], + SuperblockGeometry = [] + }, + SegmentationNeighborMap = [], + ModeInfoGrid = new Av1ModeInfo[16], + ModeInfoAllocation = allocation, + ModeInfoStride = 4, + Disallow4x4AllFrames = disallow4x4, + CdefPreset = [] + }; + + Av1MacroBlockModeInfo result = picture.GetMacroBlockModeInfo(new Point(column, row)); + + Assert.Same(expected.MacroBlockModeInfo, result); + Assert.Same(expected, picture.ModeInfoGrid[gridOffset]); + } + + [Fact] + public void MacroBlockReadsNeighborsRelativeToCurrentGridEntry() + { + Av1ModeInfo above = CreateModeInfo(Av1PredictionMode.Vertical); + Av1ModeInfo left = CreateModeInfo(Av1PredictionMode.Horizontal); + Av1ModeInfo current = CreateModeInfo(Av1PredictionMode.DC); + Av1ModeInfo[] grid = new Av1ModeInfo[9]; + grid[1] = above; + grid[3] = left; + grid[4] = current; + ObuTileGroupHeader tiles = new() + { + TileColumnCount = 1, + TileRowCount = 1 + }; + + tiles.TileColumnStartModeInfo[1] = 3; + tiles.TileRowStartModeInfo[1] = 3; + ObuFrameHeader frameHeader = new() + { + ModeInfoColumnCount = 3, + ModeInfoRowCount = 3, + TilesInfo = tiles + }; + + Av1MacroBlockD macroBlock = new() { Tile = new Av1TileInfo(0, 0, frameHeader) }; + macroBlock.SetModeInfoGrid(grid, 4); + + Assert.Same(left, macroBlock.GetRelativeModeInfo(-1)); + Assert.Same(above, macroBlock.GetRelativeModeInfo(-3)); + Assert.Same(current, macroBlock.GetRelativeModeInfo(0)); + } + + [Fact] + public void EncoderBlockModeInfoStoresSelectedSyntax() + { + Av1EncoderBlockModeInfo modeInfo = new(); + + Assert.False(modeInfo.Skip); + Assert.False(modeInfo.SkipMode); + Assert.False(modeInfo.UseIntraBlockCopy); + + modeInfo.Skip = true; + modeInfo.SkipMode = true; + modeInfo.UseIntraBlockCopy = true; + modeInfo.BlockSize = Av1BlockSize.Block16x16; + modeInfo.PartitionType = Av1PartitionType.Split; + modeInfo.SegmentId = 3; + modeInfo.Mode = Av1PredictionMode.Smooth; + modeInfo.UvMode = Av1ChromaPredictionMode.Smooth; + + Assert.True(modeInfo.Skip); + Assert.True(modeInfo.SkipMode); + Assert.True(modeInfo.UseIntraBlockCopy); + Assert.Equal(Av1BlockSize.Block16x16, modeInfo.BlockSize); + Assert.Equal(Av1PartitionType.Split, modeInfo.PartitionType); + Assert.Equal(3, modeInfo.SegmentId); + Assert.Equal(Av1PredictionMode.Smooth, modeInfo.Mode); + Assert.Equal(Av1ChromaPredictionMode.Smooth, modeInfo.UvMode); + } + [Fact] public void RoundTripZeroEndOfBlock() { @@ -277,6 +385,15 @@ public class Av1CoefficientsEntropyTests } } + private static Av1ModeInfo CreateModeInfo(Av1PredictionMode mode) + => new() + { + MacroBlockModeInfo = new Av1MacroBlockModeInfo + { + Block = new Av1EncoderBlockModeInfo { Mode = mode } + } + }; + public static TheoryData GetTransformTypes() { TheoryData result = [];