@ -1,35 +1,106 @@
// Copyright (c) Six Labors.
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
// Licensed under the Six Labors Split License.
using System.Buffers ;
using SixLabors.ImageSharp.Formats.Heif.Av1.Motion ;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit ;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit ;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction ;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform ;
using SixLabors.ImageSharp.Memory ;
using SixLabors.ImageSharp.Memory ;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling ;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling ;
/// <summary>
/// Identifies the inter-prediction features selected by decoded AV1 block syntax.
/// </summary>
[Flags]
internal enum Av1InterPredictionFeatures
{
/// <summary>
/// No tracked inter-prediction feature was selected.
/// </summary>
None = 0 ,
/// <summary>
/// Distance-weighted compound prediction was selected.
/// </summary>
DistanceWeightedCompound = 1 < < 0 ,
/// <summary>
/// A non-inverted wedge compound mask was selected.
/// </summary>
WedgeCompound = 1 < < 1 ,
/// <summary>
/// An inverted wedge compound mask was selected.
/// </summary>
InvertedWedgeCompound = 1 < < 2 ,
/// <summary>
/// The first difference-weighted compound mask orientation was selected.
/// </summary>
DifferenceWeightedCompound = 1 < < 3 ,
/// <summary>
/// The inverted difference-weighted compound mask orientation was selected.
/// </summary>
InvertedDifferenceWeightedCompound = 1 < < 4 ,
/// <summary>
/// Smooth inter-intra prediction was selected.
/// </summary>
SmoothInterIntra = 1 < < 5 ,
/// <summary>
/// Wedge inter-intra prediction was selected.
/// </summary>
WedgeInterIntra = 1 < < 6 ,
/// <summary>
/// Overlapping motion compensation was selected.
/// </summary>
Obmc = 1 < < 7 ,
/// <summary>
/// Local warped-motion prediction was selected.
/// </summary>
LocalWarp = 1 < < 8 ,
/// <summary>
/// Non-translational global warped-motion prediction was selected.
/// </summary>
GlobalWarp = 1 < < 9
}
/// <summary>
/// <summary>
/// Owns the mode, motion, segmentation, transform, coefficient, quantizer, and filter state decoded for one AV1 frame.
/// Owns the mode, motion, segmentation, transform, coefficient, quantizer, and filter state decoded for one AV1 frame.
/// </summary>
/// </summary>
internal partial class Av1FrameInfo : IDisposable
internal sealed partial class Av1FrameInfo : IDisposable
{
{
/// <summary>
/// The allocator that owns frame-sized syntax and retained-reference state.
/// </summary>
private readonly MemoryAllocator memoryAllocator ;
/// <summary>
/// <summary>
/// The coefficient slots reserved for one 4x4 mode-information unit: one end index followed by 16 coefficients.
/// The coefficient slots reserved for one 4x4 mode-information unit: one end index followed by 16 coefficients.
/// </summary>
/// </summary>
public const int CoefficientCountPerModeInfo = 1 + 1 6 ;
public const int CoefficientCountPerModeInfo = 1 + 1 6 ;
/// <summary>
/// <summary>
/// Stores raster-ordered luma coefficients for every frame superblock.
/// Owns the luma and chroma coefficient scratch for the superblock currently being decoded .
/// </summary>
/// </summary>
private readonly int [ ] coefficientsY = [ ] ;
private readonly IMemoryOwner < int > coefficientScratch ;
/// <summary>
/// <summary>
/// Stores raster-ordered blue-difference chroma coefficients for every frame superblock.
/// The number of luma coefficient entries at the start of <see cref="coefficientScratch"/> .
/// </summary>
/// </summary>
private readonly int [ ] coefficientsU = [ ] ;
private readonly int lumaCoefficientCount ;
/// <summary>
/// <summary>
/// Stores raster-ordered red-difference chroma coefficients for every frame superblock.
/// The number of coefficient entries reserved for each chroma plane .
/// </summary>
/// </summary>
private readonly int [ ] coefficientsV = [ ] ;
private readonly int chromaCoefficientCount ;
/// <summary>
/// <summary>
/// The width and height of a superblock in 4x4 mode-information units.
/// The width and height of a superblock in 4x4 mode-information units.
@ -57,14 +128,14 @@ internal partial class Av1FrameInfo : IDisposable
private readonly int subsamplingFactor ;
private readonly int subsamplingFactor ;
/// <summary>
/// <summary>
/// Stores one addressing view for each frame superblock .
/// Stores decoded block mode information in bitstream traversal order .
/// </summary>
/// </summary>
private readonly Av1SuperblockInfo [ ] superblock Infos;
private readonly MemoryGroup < Av1BlockModeInfo > mode Infos;
/// <summary>
/// <summary>
/// Stores decoded block mode information in bitstream traversal order .
/// Stores the number of mode-information records written to each superblock row in <see cref="modeInfos"/> .
/// </summary>
/// </summary>
private readonly Av1BlockModeInfo [ ] modeInfo s;
private readonly Buffer2D < int > modeInfoCount s;
/// <summary>
/// <summary>
/// Maps every frame-relative 4x4 position to its covering entry in <see cref="modeInfos"/>.
/// Maps every frame-relative 4x4 position to its covering entry in <see cref="modeInfos"/>.
@ -74,7 +145,7 @@ internal partial class Av1FrameInfo : IDisposable
/// <summary>
/// <summary>
/// Stores the decoded segment identifier for each active 4x4 mode-information position in row-major order.
/// Stores the decoded segment identifier for each active 4x4 mode-information position in row-major order.
/// </summary>
/// </summary>
private byte [ ] segmentIds = [ ] ;
private Buffer2D < byte > ? segmentIds ;
/// <summary>
/// <summary>
/// The number of active 4x4 columns in one row of <see cref="segmentIds"/>.
/// The number of active 4x4 columns in one row of <see cref="segmentIds"/>.
@ -87,19 +158,14 @@ internal partial class Av1FrameInfo : IDisposable
private int segmentIdRowCount ;
private int segmentIdRowCount ;
/// <summary>
/// <summary>
/// Stores luma transform information grouped by superblock.
/// Owns the luma and shared-chroma transform-information scratch for the superblock currently being decoded.
/// </summary>
private readonly Av1TransformInfo [ ] transformInfosY ;
/// <summary>
/// Stores both chroma planes' transform information grouped by superblock.
/// </summary>
/// </summary>
private readonly Av1TransformInfo [ ] transformInfosUv ;
private readonly IMemoryOwner < Av1TransformInfo > transformInfoScratch ;
/// <summary>
/// <summary>
/// Stores the active base quantizer index for each frame superblock.
/// Stores the active base quantizer index for each frame superblock.
/// </summary>
/// </summary>
private readonly int [ ] quantizerIndices ;
private readonly Buffer2D < int > quantizerIndices ;
/// <summary>
/// <summary>
/// The base-2 number of constrained directional enhancement filter entries allocated per superblock.
/// The base-2 number of constrained directional enhancement filter entries allocated per superblock.
@ -109,7 +175,7 @@ internal partial class Av1FrameInfo : IDisposable
/// <summary>
/// <summary>
/// Stores constrained directional enhancement filter strengths grouped by superblock.
/// Stores constrained directional enhancement filter strengths grouped by superblock.
/// </summary>
/// </summary>
private readonly int [ ] cdefStrength ;
private readonly Buffer2D < int > cdefStrength ;
/// <summary>
/// <summary>
/// The base-2 number of loop-filter delta values stored per superblock.
/// The base-2 number of loop-filter delta values stored per superblock.
@ -119,99 +185,259 @@ internal partial class Av1FrameInfo : IDisposable
/// <summary>
/// <summary>
/// Stores the four loop-filter delta values for each superblock.
/// Stores the four loop-filter delta values for each superblock.
/// </summary>
/// </summary>
private readonly int [ ] deltaLoopFilter ;
private readonly Buffer2D < int > deltaLoopFilter ;
/// <summary>
/// <summary>
/// Stores raster-ordered loop-restoration units for each color plane.
/// Stores raster-ordered loop-restoration units for each color plane.
/// </summary>
/// </summary>
private readonly Av1LoopRestorationUnit [ ] [ ] loopRestorationUnits = [ [ ] , [ ] , [ ] ] ;
private InlineArray4 < Buffer2D < Av1LoopRestorationUnit > ? > loopRestorationUnits ;
/// <summary>
/// <summary>
/// Stores the number of loop-restoration unit columns for each color plane.
/// Stores the number of loop-restoration unit columns for each color plane.
/// </summary>
/// </summary>
private readonly int [ ] loopRestorationUnitColumns = new int [ Av1Constants . MaxPlanes ] ;
private InlineArray4 < int > loopRestorationUnitColumns ;
/// <summary>
/// <summary>
/// The number of loop-restoration unit rows allocated for each color plane.
/// The number of loop-restoration unit rows allocated for each color plane.
/// </summary>
/// </summary>
private readonly int [ ] loopRestorationUnitRows = new int [ Av1Constants . MaxPlanes ] ;
private InlineArray4 < int > loopRestorationUnitRows ;
/// <summary>
/// <summary>
/// Initializes a new instance of the <see cref="Av1FrameInfo"/> class.
/// Initializes a new instance of the <see cref="Av1FrameInfo"/> class using sequence-maximum dimensions .
/// </summary>
/// </summary>
/// <param name="sequenceHeader">The sequence header defining maximum dimensions, superblock size, and color sampling.</param>
/// <param name="sequenceHeader">The sequence header defining maximum dimensions, superblock size, and color sampling.</param>
public Av1FrameInfo ( ObuSequenceHeader sequenceHeader )
public Av1FrameInfo ( ObuSequenceHeader sequenceHeader )
: this ( Configuration . Default , sequenceHeader , sequenceHeader . MaxFrameWidth , sequenceHeader . MaxFrameHeight )
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Av1FrameInfo"/> class for one active coded frame.
/// </summary>
/// <param name="sequenceHeader">The sequence header defining superblock size and color sampling.</param>
/// <param name="frameHeader">The frame header defining the active coded dimensions.</param>
public Av1FrameInfo ( ObuSequenceHeader sequenceHeader , ObuFrameHeader frameHeader )
: this (
Configuration . Default ,
sequenceHeader ,
frameHeader . FrameSize . FrameWidth ,
frameHeader . FrameSize . FrameHeight )
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Av1FrameInfo"/> class for one active coded frame.
/// </summary>
/// <param name="configuration">The decoder configuration providing frame-sized storage.</param>
/// <param name="sequenceHeader">The sequence header defining superblock size and color sampling.</param>
/// <param name="frameHeader">The frame header defining the active coded dimensions.</param>
public Av1FrameInfo ( Configuration configuration , ObuSequenceHeader sequenceHeader , ObuFrameHeader frameHeader )
: this (
configuration ,
sequenceHeader ,
frameHeader . FrameSize . FrameWidth ,
frameHeader . FrameSize . FrameHeight )
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Av1FrameInfo"/> class for explicit coded dimensions.
/// </summary>
/// <param name="configuration">The decoder configuration providing frame-sized storage.</param>
/// <param name="sequenceHeader">The sequence header defining superblock size and color sampling.</param>
/// <param name="frameWidth">The active coded frame width.</param>
/// <param name="frameHeight">The active coded frame height.</param>
private Av1FrameInfo ( Configuration configuration , ObuSequenceHeader sequenceHeader , int frameWidth , int frameHeight )
{
{
// Size frame-owned storage from the sequence maximums because later frame headers may select
// A FrameInfo instance belongs to one coded frame, so transient syntax storage follows that frame rather
// any coded dimensions up to these bounds without rebuilding the decoder's indexing model.
// than the potentially much larger sequence maximum declared by an untrusted stream.
this . memoryAllocator = configuration . MemoryAllocator ;
int superblockSizeLog2 = sequenceHeader . SuperblockSizeLog2 ;
int superblockSizeLog2 = sequenceHeader . SuperblockSizeLog2 ;
int superblockAlignedWidth = Av1Math . AlignPowerOf2 ( sequenceHeader . MaxFrameWidth , superblockSizeLog2 ) ;
int superblockAlignedWidth = Av1Math . AlignPowerOf2 ( f rameWidth, superblockSizeLog2 ) ;
int superblockAlignedHeight = Av1Math . AlignPowerOf2 ( sequenceHeader . MaxFrameHeight , superblockSizeLog2 ) ;
int superblockAlignedHeight = Av1Math . AlignPowerOf2 ( f rameHeight, superblockSizeLog2 ) ;
this . superblockColumnCount = superblockAlignedWidth > > superblockSizeLog2 ;
this . superblockColumnCount = superblockAlignedWidth > > superblockSizeLog2 ;
this . superblockRowCount = superblockAlignedHeight > > superblockSizeLog2 ;
this . superblockRowCount = superblockAlignedHeight > > superblockSizeLog2 ;
int superblockCount = this . superblockColumnCount * this . superblockRowCount ;
int superblockCount = this . superblockColumnCount * this . superblockRowCount ;
this . modeInfoSizePerSuperblock = 1 < < ( superblockSizeLog2 - Av1Constants . ModeInfoSizeLog2 ) ;
this . modeInfoSizePerSuperblock = 1 < < ( superblockSizeLog2 - Av1Constants . ModeInfoSizeLog2 ) ;
this . modeInfoCountPerSuperblock = this . modeInfoSizePerSuperblock * this . modeInfoSizePerSuperblock ;
this . modeInfoCountPerSuperblock = this . modeInfoSizePerSuperblock * this . modeInfoSizePerSuperblock ;
int numPlanes = sequenceHeader . ColorConfig . IsMonochrome ? 1 : Av1Constants . MaxPlanes ;
// A decoded block can cover multiple 4x4 positions, so modeInfos stores each block once while
// modeInfoMap makes every covered position resolve to that single traversal-order entry.
this . superblockInfos = new Av1SuperblockInfo [ superblockCount ] ;
this . modeInfos = new Av1BlockModeInfo [ superblockCount * this . modeInfoCountPerSuperblock ] ;
this . modeInfoMap = new Av1FrameModeInfoMap ( new Size ( this . modeInfoSizePerSuperblock * this . superblockColumnCount , this . modeInfoSizePerSuperblock * this . superblockRowCount ) ) ;
this . transformInfosY = new Av1TransformInfo [ superblockCount * this . modeInfoCountPerSuperblock ] ;
this . transformInfosUv = new Av1TransformInfo [ 2 * superblockCount * this . modeInfoCountPerSuperblock ] ;
// Superblock views retain only their grid position and address all storage through this owner.
int i = 0 ;
for ( int y = 0 ; y < this . superblockRowCount ; y + + )
{
for ( int x = 0 ; x < this . superblockColumnCount ; x + + )
{
Point point = new ( x , y ) ;
this . superblockInfos [ i ] = new ( this , point ) ;
i + + ;
}
}
bool subX = sequenceHeader . ColorConfig . SubSamplingX ;
bool subX = sequenceHeader . ColorConfig . SubSamplingX ;
bool subY = sequenceHeader . ColorConfig . SubSamplingY ;
bool subY = sequenceHeader . ColorConfig . SubSamplingY ;
// Chroma capacity scales by two for each sampled axis: 4:4:4 => 0, 4:2:2 => 1, 4:2:0 => 2.
// Chroma capacity scales by two for each sampled axis: 4:4:4 => 0, 4:2:2 => 1, 4:2:0 => 2.
this . subsamplingFactor = ( subX & & subY ) ? 2 : ( subX & & ! subY ) ? 1 : ( ! subX & & ! subY ) ? 0 : - 1 ;
this . subsamplingFactor = ( subX & & subY ) ? 2 : ( subX & & ! subY ) ? 1 : ( ! subX & & ! subY ) ? 0 : - 1 ;
Guard . IsFalse ( this . subsamplingFactor = = - 1 , nameof ( this . subsamplingFactor ) , "Invalid combination of subsampling." ) ;
Guard . IsFalse ( this . subsamplingFactor = = - 1 , nameof ( this . subsamplingFactor ) , "Invalid combination of subsampling." ) ;
int lumaCoefficientCountPerSuperblock = this . modeInfoCountPerSuperblock * CoefficientCountPerModeInfo ;
this . lumaCoefficientCount = this . modeInfoCountPerSuperblock * CoefficientCountPerModeInfo ;
int chromaCoefficientCountPerSuperblock = lumaCoefficientCountPerSuperblock > > this . subsamplingFactor ;
this . chromaCoefficientCount = sequenceHeader . ColorConfig . IsMonochrome
this . coefficientsY = new int [ superblockCount * lumaCoefficientCountPerSuperblock ] ;
? 0
this . coefficientsU = new int [ superblockCount * chromaCoefficientCountPerSuperblock ] ;
: this . lumaCoefficientCount > > this . subsamplingFactor ;
this . coefficientsV = new int [ superblockCount * chromaCoefficientCountPerSuperblock ] ;
this . quantizerIndices = new int [ superblockCount ] ;
IMemoryOwner < int > ? allocatedCoefficientScratch = null ;
MemoryGroup < Av1BlockModeInfo > ? allocatedModeInfos = null ;
// A 128x128 superblock contains four 64x64 CDEF filter blocks; a 64x64 superblock contains one.
Buffer2D < int > ? allocatedModeInfoCounts = null ;
this . cdefStrengthFactorLog2 = ( superblockSizeLog2 - 6 ) < < 1 ;
Av1FrameModeInfoMap ? allocatedModeInfoMap = null ;
this . cdefStrength = new int [ superblockCount < < this . cdefStrengthFactorLog2 ] ;
IMemoryOwner < Av1TransformInfo > ? allocatedTransformInfoScratch = null ;
Array . Fill ( this . cdefStrength , - 1 ) ;
Buffer2D < int > ? allocatedQuantizerIndices = null ;
this . deltaLoopFilter = new int [ superblockCount < < this . deltaLoopFactorLog2 ] ;
Buffer2D < int > ? allocatedCdefStrength = null ;
Buffer2D < int > ? allocatedDeltaLoopFilter = null ;
try
{
// Reconstruction consumes one superblock before parsing the next. One allocator-owned scratch surface
// therefore covers all three planes without per-block arrays or unmanaged ownership outside ImageSharp.
int coefficientScratchLength = checked ( this . lumaCoefficientCount + ( 2 * this . chromaCoefficientCount ) ) ;
allocatedCoefficientScratch = this . memoryAllocator . Allocate < int > ( coefficientScratchLength , AllocationOptions . Clean ) ;
// A decoded block can cover multiple 4x4 positions. Each allocator-backed row stores one
// superblock's traversal records, while the map resolves every covered 4x4 position to them.
AllocationOptions clean = AllocationOptions . Clean ;
// Mode information survives until frame completion, but one superblock row is much larger than a
// constrained allocator segment. Store it as a discontiguous group and address records by packed index.
long modeInfoLength = ( long ) this . modeInfoCountPerSuperblock * superblockCount ;
allocatedModeInfos = this . memoryAllocator . AllocateGroup < Av1BlockModeInfo > ( modeInfoLength , 1 , clean ) ;
allocatedModeInfoCounts = this . memoryAllocator . Allocate2D < int > ( 1 , superblockCount , clean ) ;
allocatedModeInfoMap = new Av1FrameModeInfoMap (
this . memoryAllocator ,
new Size (
this . modeInfoSizePerSuperblock * this . superblockColumnCount ,
this . modeInfoSizePerSuperblock * this . superblockRowCount ) ) ;
// Tile parsing reconstructs each superblock before advancing to the next one. A single three-plane
// scratch owner therefore preserves every active transform while avoiding frame-wide retained copies.
int transformInfoScratchLength = checked ( this . modeInfoCountPerSuperblock * 3 ) ;
allocatedTransformInfoScratch = this . memoryAllocator . Allocate < Av1TransformInfo > ( transformInfoScratchLength , clean ) ;
allocatedQuantizerIndices = this . memoryAllocator . Allocate2D < int > ( 1 , superblockCount , clean ) ;
// A 128x128 superblock contains four 64x64 CDEF filter blocks; a 64x64 superblock contains one.
this . cdefStrengthFactorLog2 = ( superblockSizeLog2 - 6 ) < < 1 ;
allocatedCdefStrength = this . memoryAllocator . Allocate2D < int > ( 1 < < this . cdefStrengthFactorLog2 , superblockCount , clean ) ;
allocatedCdefStrength . MemoryGroup . Fill ( - 1 ) ;
allocatedDeltaLoopFilter = this . memoryAllocator . Allocate2D < int > ( 1 < < this . deltaLoopFactorLog2 , superblockCount , clean ) ;
}
catch
{
allocatedDeltaLoopFilter ? . Dispose ( ) ;
allocatedCdefStrength ? . Dispose ( ) ;
allocatedQuantizerIndices ? . Dispose ( ) ;
allocatedTransformInfoScratch ? . Dispose ( ) ;
allocatedModeInfoMap ? . Dispose ( ) ;
allocatedModeInfoCounts ? . Dispose ( ) ;
allocatedModeInfos ? . Dispose ( ) ;
allocatedCoefficientScratch ? . Dispose ( ) ;
throw ;
}
this . coefficientScratch = allocatedCoefficientScratch ;
this . modeInfos = allocatedModeInfos ;
this . modeInfoCounts = allocatedModeInfoCounts ;
this . modeInfoMap = allocatedModeInfoMap ;
this . transformInfoScratch = allocatedTransformInfoScratch ;
this . quantizerIndices = allocatedQuantizerIndices ;
this . cdefStrength = allocatedCdefStrength ;
this . deltaLoopFilter = allocatedDeltaLoopFilter ;
}
}
/// <summary>
/// <summary>
/// Gets the total mode-information capacity allocated for the frame.
/// Gets the total mode-information capacity allocated for the frame.
/// </summary>
/// </summary>
public int ModeInfoCount = > this . modeInfos . Length ;
public int ModeInfoCount = > checked ( ( int ) this . modeInfos . Total Length) ;
/// <summary>
/// <summary>
/// Gets the width or height of one square superblock in 4x4 mode-information units.
/// Gets the width or height of one square superblock in 4x4 mode-information units.
/// </summary>
/// </summary>
public int SuperblockModeInfoSize = > this . modeInfoSizePerSuperblock ;
public int SuperblockModeInfoSize = > this . modeInfoSizePerSuperblock ;
/// <summary>
/// Gets a bit mask containing every luma transform type decoded in this frame.
/// </summary>
public int LumaTransformTypeCoverage { get ; private set ; }
/// <summary>
/// Gets the inter-prediction features selected by coding blocks in this frame.
/// </summary>
public Av1InterPredictionFeatures InterPredictionFeatures { get ; private set ; }
/// <summary>
/// Records one decoded luma transform type before the current-superblock scratch is reused.
/// </summary>
/// <param name="transformType">The decoded luma transform type.</param>
public void RecordLumaTransformType ( Av1TransformType transformType ) = >
this . LumaTransformTypeCoverage | = 1 < < ( int ) transformType ;
/// <summary>
/// Records the inter-prediction features selected by one completed coding block.
/// </summary>
/// <param name="modeInfo">The completed block mode information.</param>
/// <param name="frameHeader">The frame header containing global-motion parameters.</param>
public void RecordInterPredictionFeatures ( Av1BlockModeInfo modeInfo , ObuFrameHeader frameHeader )
{
if ( modeInfo . ReferenceFrames [ 0 ] < = Av1ReferenceFrameType . Intra )
{
return ;
}
Av1InterPredictionFeatures features = Av1InterPredictionFeatures . None ;
if ( modeInfo . MotionMode = = Av1MotionMode . Obmc )
{
features | = Av1InterPredictionFeatures . Obmc ;
}
if ( modeInfo . MotionMode = = Av1MotionMode . Warped )
{
features | = Av1InterPredictionFeatures . LocalWarp ;
}
if ( modeInfo . YMode is Av1PredictionMode . GlobalMotionVector or Av1PredictionMode . GlobalGlobalMotionVector & &
Math . Min ( modeInfo . BlockSize . GetWidth ( ) , modeInfo . BlockSize . GetHeight ( ) ) > = 8 )
{
int referenceCount = modeInfo . ReferenceFrames [ 1 ] > Av1ReferenceFrameType . Intra ? 2 : 1 ;
Span < Av1GlobalMotionParameters > globalMotionParameters = frameHeader . GetGlobalMotionParameters ( ) ;
for ( int referenceIndex = 0 ; referenceIndex < referenceCount ; referenceIndex + + )
{
int canonicalReferenceIndex =
( int ) modeInfo . ReferenceFrames [ referenceIndex ] - ( int ) Av1ReferenceFrameType . Last ;
Av1GlobalMotionParameters parameters = globalMotionParameters [ canonicalReferenceIndex ] ;
if ( parameters . Type > Av1GlobalMotionType . Translation & & ! parameters . IsInvalid )
{
features | = Av1InterPredictionFeatures . GlobalWarp ;
}
}
}
if ( modeInfo . ReferenceFrames [ 1 ] = = Av1ReferenceFrameType . Intra )
{
features | = modeInfo . UseInterIntraWedge
? Av1InterPredictionFeatures . WedgeInterIntra
: Av1InterPredictionFeatures . SmoothInterIntra ;
}
else if ( modeInfo . ReferenceFrames [ 1 ] > Av1ReferenceFrameType . Intra )
{
features | = modeInfo . CompoundType switch
{
Av1CompoundType . DistanceWeighted = > Av1InterPredictionFeatures . DistanceWeightedCompound ,
Av1CompoundType . Wedge = > modeInfo . CompoundWedgeSign
? Av1InterPredictionFeatures . InvertedWedgeCompound
: Av1InterPredictionFeatures . WedgeCompound ,
Av1CompoundType . DifferenceWeighted = > modeInfo . DifferenceWeightedMaskType = = Av1DifferenceWeightedMaskType . Type38Inverse
? Av1InterPredictionFeatures . InvertedDifferenceWeightedCompound
: Av1InterPredictionFeatures . DifferenceWeightedCompound ,
_ = > Av1InterPredictionFeatures . None ,
} ;
}
this . InterPredictionFeatures | = features ;
}
/// <summary>
/// <summary>
/// Initializes the active frame's contiguous segment map and applies whole-map inheritance when requested.
/// Initializes the active frame's contiguous segment map and applies whole-map inheritance when requested.
/// </summary>
/// </summary>
/// <param name="frameHeader">The frame header defining active geometry and segmentation update behavior.</param>
/// <param name="frameHeader">The frame header defining active geometry and segmentation update behavior.</param>
/// <param name="primaryReferenceFrameInfo">
/// <param name="primaryReferenceState ">
/// The retained state selected by the primary reference, or <see langword="null"/> when no primary reference exists.
/// The retained state selected by the primary reference, or <see langword="null"/> when no primary reference exists.
/// </param>
/// </param>
public void InitializeSegmentIds ( ObuFrameHeader frameHeader , Av1FrameInfo ? primaryReferenceFrameInfo )
public void InitializeSegmentIds ( ObuFrameHeader frameHeader , ReferenceState ? primaryReferenceState )
{
{
ObuSegmentationParameters segmentationParameters = frameHeader . SegmentationParameters ;
ObuSegmentationParameters segmentationParameters = frameHeader . SegmentationParameters ;
if ( ! segmentationParameters . Enabled )
if ( ! segmentationParameters . Enabled )
@ -223,16 +449,21 @@ internal partial class Av1FrameInfo : IDisposable
this . segmentIdColumnCount = frameHeader . ModeInfoColumnCount ;
this . segmentIdColumnCount = frameHeader . ModeInfoColumnCount ;
this . segmentIdRowCount = frameHeader . ModeInfoRowCount ;
this . segmentIdRowCount = frameHeader . ModeInfoRowCount ;
this . segmentIds = new byte [ this . segmentIdColumnCount * this . segmentIdRowCount ] ;
this . segmentIds = this . memoryAllocator . Allocate2D < byte > (
this . segmentIdColumnCount ,
this . segmentIdRowCount ,
AllocationOptions . Clean ) ;
Buffer2D < byte > ? primarySegmentIds = primaryReferenceState ? . SegmentIds ;
if ( segmentationParameters . SegmentationUpdateMap = = 0 & &
if ( segmentationParameters . SegmentationUpdateMap = = 0 & &
primaryReferenceFrameInfo is not null & &
primaryReferenceState is not null & &
primaryReferenceFrameInfo . segmentIdColumnCount = = this . segmentIdColumnCount & &
primarySegmentIds is not null & &
primaryReferenceFrameInfo . segmentIdRowCount = = this . segmentIdRowCount )
primaryReferenceState . SegmentIdColumnCount = = this . segmentIdColumnCount & &
primaryReferenceState . SegmentIdRowCount = = this . segmentIdRowCount )
{
{
// AV1 decodemv.c copies the selected primary frame's block coverage when update_map is zero. Copying the
// AV1 decodemv.c copies the selected primary frame's block coverage when update_map is zero. Copying the
// same contiguous map once establishes the identical final state without repeating a row copy per block.
// same contiguous map once establishes the identical final state without repeating a row copy per block.
primaryReferenceFrameInfo . s egmentIds . CopyTo ( this . segmentIds , 0 ) ;
primaryS egmentIds . CopyTo ( this . segmentIds ) ;
}
}
}
}
@ -242,12 +473,19 @@ internal partial class Av1FrameInfo : IDisposable
/// <param name="row">The zero-based mode-information row.</param>
/// <param name="row">The zero-based mode-information row.</param>
/// <param name="column">The zero-based mode-information column.</param>
/// <param name="column">The zero-based mode-information column.</param>
/// <returns>The segment identifier stored at the requested position.</returns>
/// <returns>The segment identifier stored at the requested position.</returns>
public byte GetSegmentId ( int row , int column ) = > this . segmentIds [ ( row * this . segmentIdColumnCount ) + column ] ;
public byte GetSegmentId ( int row , int column )
{
Buffer2D < byte > segmentIds = this . segmentIds
? ? this . referenceState ? . SegmentIds
? ? throw new InvalidOperationException ( "The AV1 frame has no active segmentation map." ) ;
return segmentIds [ column , row ] ;
}
/// <summary>
/// <summary>
/// Gets the minimum retained segment identifier across a block's clipped mode-information coverage.
/// Gets the minimum retained segment identifier across a block's clipped mode-information coverage.
/// </summary>
/// </summary>
/// <param name="primaryReferenceFrameInfo">
/// <param name="primaryReferenceState ">
/// The retained primary-frame state, or <see langword="null"/> when no compatible map is available.
/// The retained primary-frame state, or <see langword="null"/> when no compatible map is available.
/// </param>
/// </param>
/// <param name="blockSize">The block size whose 4x4 coverage is inspected.</param>
/// <param name="blockSize">The block size whose 4x4 coverage is inspected.</param>
@ -255,12 +493,13 @@ internal partial class Av1FrameInfo : IDisposable
/// <returns>
/// <returns>
/// The minimum retained segment identifier, or zero when no same-sized retained segmentation map is available.
/// The minimum retained segment identifier, or zero when no same-sized retained segmentation map is available.
/// </returns>
/// </returns>
public int GetPredictedSegmentId ( Av1FrameInfo ? primaryReferenceFrameInfo , Av1BlockSize blockSize , Point modeInfoPosition )
public int GetPredictedSegmentId ( ReferenceState ? primaryReferenceState , Av1BlockSize blockSize , Point modeInfoPosition )
{
{
if ( primaryReferenceFrameInfo is null | |
Buffer2D < byte > ? primarySegmentIds = primaryReferenceState ? . SegmentIds ;
primaryReferenceFrameInfo . segmentIds . Length = = 0 | |
if ( primaryReferenceState is null | |
primaryReferenceFrameInfo . segmentIdColumnCount ! = this . segmentIdColumnCount | |
primarySegmentIds is null | |
primaryReferenceFrameInfo . segmentIdRowCount ! = this . segmentIdRowCount )
primaryReferenceState . SegmentIdColumnCount ! = this . segmentIdColumnCount | |
primaryReferenceState . SegmentIdRowCount ! = this . segmentIdRowCount )
{
{
// the reference decoder exposes the prior map only when both mode-info dimensions match the active frame. Treating a
// the reference decoder exposes the prior map only when both mode-info dimensions match the active frame. Treating a
// differently sized retained map as absent prevents coordinates from being reinterpreted with a new stride.
// differently sized retained map as absent prevents coordinates from being reinterpreted with a new stride.
@ -275,8 +514,9 @@ internal partial class Av1FrameInfo : IDisposable
// dec_get_segment_id rule used when segmentation_temporal_update selects the retained primary map.
// dec_get_segment_id rule used when segmentation_temporal_update selects the retained primary map.
for ( int row = 0 ; row < rowCount ; row + + )
for ( int row = 0 ; row < rowCount ; row + + )
{
{
int offset = ( ( modeInfoPosition . Y + row ) * primaryReferenceFrameInfo . segmentIdColumnCount ) + modeInfoPosition . X ;
ReadOnlySpan < byte > segmentRow = primarySegmentIds
ReadOnlySpan < byte > segmentRow = primaryReferenceFrameInfo . segmentIds . AsSpan ( offset , columnCount ) ;
. DangerousGetRowSpan ( modeInfoPosition . Y + row )
. Slice ( modeInfoPosition . X , columnCount ) ;
for ( int column = 0 ; column < segmentRow . Length ; column + + )
for ( int column = 0 ; column < segmentRow . Length ; column + + )
{
{
@ -297,13 +537,17 @@ internal partial class Av1FrameInfo : IDisposable
{
{
int columnCount = Math . Min ( blockSize . Get4x4WideCount ( ) , this . segmentIdColumnCount - modeInfoPosition . X ) ;
int columnCount = Math . Min ( blockSize . Get4x4WideCount ( ) , this . segmentIdColumnCount - modeInfoPosition . X ) ;
int rowCount = Math . Min ( blockSize . Get4x4HighCount ( ) , this . segmentIdRowCount - modeInfoPosition . Y ) ;
int rowCount = Math . Min ( blockSize . Get4x4HighCount ( ) , this . segmentIdRowCount - modeInfoPosition . Y ) ;
Buffer2D < byte > segmentIds = this . segmentIds
? ? throw new InvalidOperationException ( "The AV1 frame has no writable segmentation map." ) ;
// Each block contributes one ID to all covered 4x4 cells. Filling contiguous row slices retains the native
// Each block contributes one ID to all covered 4x4 cells. Filling contiguous row slices retains the native
// row-major layout without the per-row object indirection of the previous jagged map.
// row-major layout without the per-row object indirection of the previous jagged map.
for ( int row = 0 ; row < rowCount ; row + + )
for ( int row = 0 ; row < rowCount ; row + + )
{
{
int offset = ( ( modeInfoPosition . Y + row ) * this . segmentIdColumnCount ) + modeInfoPosition . X ;
segmentIds
this . segmentIds . AsSpan ( offset , columnCount ) . Fill ( ( byte ) segmentId ) ;
. DangerousGetRowSpan ( modeInfoPosition . Y + row )
. Slice ( modeInfoPosition . X , columnCount )
. Fill ( ( byte ) segmentId ) ;
}
}
}
}
@ -333,15 +577,12 @@ internal partial class Av1FrameInfo : IDisposable
// unit count to nearest instead of unconditionally rounding a partial unit upward.
// unit count to nearest instead of unconditionally rounding a partial unit upward.
int columnCount = Math . Max ( ( planeWidth + ( item . Size > > 1 ) ) / item . Size , 1 ) ;
int columnCount = Math . Max ( ( planeWidth + ( item . Size > > 1 ) ) / item . Size , 1 ) ;
int rowCount = Math . Max ( ( planeHeight + ( item . Size > > 1 ) ) / item . Size , 1 ) ;
int rowCount = Math . Max ( ( planeHeight + ( item . Size > > 1 ) ) / item . Size , 1 ) ;
Av1LoopRestorationUnit [ ] units = new Av1LoopRestorationUnit [ columnCount * rowCount ] ;
for ( int i = 0 ; i < units . Length ; i + + )
{
units [ i ] = new ( ) ;
}
this . loopRestorationUnitColumns [ planeIndex ] = columnCount ;
this . loopRestorationUnitColumns [ planeIndex ] = columnCount ;
this . loopRestorationUnitRows [ planeIndex ] = rowCount ;
this . loopRestorationUnitRows [ planeIndex ] = rowCount ;
this . loopRestorationUnits [ planeIndex ] = units ;
this . loopRestorationUnits [ planeIndex ] = this . memoryAllocator . Allocate2D < Av1LoopRestorationUnit > (
columnCount ,
rowCount ,
AllocationOptions . Clean ) ;
}
}
}
}
@ -350,11 +591,17 @@ internal partial class Av1FrameInfo : IDisposable
/// </summary>
/// </summary>
/// <param name="index">The position in the frame superblock grid.</param>
/// <param name="index">The position in the frame superblock grid.</param>
/// <returns>The superblock view.</returns>
/// <returns>The superblock view.</returns>
public Av1SuperblockInfo GetSuperblock ( Point index )
public Av1SuperblockInfo GetSuperblock ( Point index ) = > new ( this , index ) ;
/// <summary>
/// Gets the number of mode-information records parsed for a specified superblock.
/// </summary>
/// <param name="index">The position in the frame superblock grid.</param>
/// <returns>The number of parsed records.</returns>
public int GetModeInfoCount ( Point index )
{
{
Span < Av1SuperblockInfo > span = this . superblockInfos ;
int storageRow = ( index . Y * this . superblockColumnCount ) + index . X ;
int i = ( index . Y * this . superblockColumnCount ) + index . X ;
return this . modeInfoCounts [ 0 , storageRow ] ;
return span [ i ] ;
}
}
/// <summary>
/// <summary>
@ -373,8 +620,7 @@ internal partial class Av1FrameInfo : IDisposable
public Av1BlockModeInfo GetModeInfo ( Point superblockIndex , Point modeInfoIndex )
public Av1BlockModeInfo GetModeInfo ( Point superblockIndex , Point modeInfoIndex )
{
{
Point location = this . GetModeInfoPosition ( superblockIndex , modeInfoIndex ) ;
Point location = this . GetModeInfoPosition ( superblockIndex , modeInfoIndex ) ;
int index = this . modeInfoMap [ location ] ;
return this . GetModeInfoByStorageIndex ( this . modeInfoMap [ location ] ) ;
return this . modeInfos [ index ] ;
}
}
/// <summary>
/// <summary>
@ -382,99 +628,74 @@ internal partial class Av1FrameInfo : IDisposable
/// </summary>
/// </summary>
/// <param name="modeInfoPosition">The frame-relative position in 4x4 mode-information units.</param>
/// <param name="modeInfoPosition">The frame-relative position in 4x4 mode-information units.</param>
/// <returns>The mode information covering the position.</returns>
/// <returns>The mode information covering the position.</returns>
public Av1BlockModeInfo GetModeInfoAt ( Point modeInfoPosition ) = > this . modeInfos [ this . modeInfoMap [ modeInfoPosition ] ] ;
public Av1BlockModeInfo GetModeInfoAt ( Point modeInfoPosition )
= > this . GetModeInfoByStorageIndex ( this . modeInfoMap [ modeInfoPosition ] ) ;
/// <summary>
/// <summary>
/// Gets the mode information records parsed for the specified superblock in bitstream order.
/// Gets the mode information records parsed for the specified superblock in bitstream order.
/// </summary>
/// </summary>
/// <param name="superblockIndex">The position in the frame superblock grid.</param>
/// <param name="superblockIndex">The position in the frame superblock grid.</param>
/// <param name="count">The number of parsed records to return.</param>
/// <param name="count">The number of parsed records to return.</param>
/// <returns>T he parsed mode-information records.</returns>
/// <returns>A discontiguous view of t he parsed mode-information records.</returns>
public Span < Av1BlockModeInfo > GetModeInfos ( Point superblockIndex , int count )
public ModeInfoCollection GetModeInfos ( Point superblockIndex , int count )
{
{
Point location = this . GetModeInfoPosition ( superblockIndex , Point . Empty ) ;
int storageRow = ( superblockIndex . Y * this . superblockColumnCount ) + superblockIndex . X ;
int index = this . modeInfoMap [ location ] ;
return new ModeInfoCollection ( this , storageRow * this . modeInfoCountPerSuperblock , count ) ;
return this . modeInfos . AsSpan ( index , count ) ;
}
}
/// <summary>
/// <summary>
/// Gets the transform-information storage for one plane of a specified superblock.
/// Gets the transform-information scratch for one plane of the current superblock.
/// </summary>
/// </summary>
/// <param name="plane">The zero-based plane index.</param>
/// <param name="plane">The zero-based plane index.</param>
/// <param name="index">The position in the frame superblock grid.</param>
/// <returns>The luma storage for plane zero; otherwise, the shared chroma storage.</returns>
/// <returns>The luma storage for plane zero; otherwise, the shared chroma storage.</returns>
public Span < Av1TransformInfo > GetSuperblockTransform ( int plane , Point index )
public Span < Av1TransformInfo > GetSuperblockTransform ( int plane )
{
{
if ( plane = = 0 )
if ( plane = = 0 )
{
{
return this . GetSuperblockTransformY ( index ) ;
return this . GetSuperblockTransformY ( ) ;
}
}
return this . GetSuperblockTransformUv ( index ) ;
return this . GetSuperblockTransformUv ( ) ;
}
}
/// <summary>
/// <summary>
/// Gets the luma transform-information storage for a specified superblock.
/// Gets the luma transform-information scratch for the current superblock.
/// </summary>
/// </summary>
/// <param name="index">The position in the frame superblock grid.</param>
/// <returns>The current-superblock luma transform-information span.</returns>
/// <returns>The superblock luma transform-information span.</returns>
public Span < Av1TransformInfo > GetSuperblockTransformY ( )
public Span < Av1TransformInfo > GetSuperblockTransformY ( Point index )
= > this . transformInfoScratch . GetSpan ( ) [ . . this . modeInfoCountPerSuperblock ] ;
{
Span < Av1TransformInfo > span = this . transformInfosY ;
int offset = ( ( index . Y * this . superblockColumnCount ) + index . X ) * this . modeInfoCountPerSuperblock ;
return span . Slice ( offset , this . modeInfoCountPerSuperblock ) ;
}
/// <summary>
/// <summary>
/// Gets the shared chroma transform-information storage for a specified superblock.
/// Gets the shared chroma transform-information scratch for the current superblock.
/// </summary>
/// </summary>
/// <param name="index">The position in the frame superblock grid.</param>
/// <returns>The current-superblock chroma transform-information span.</returns>
/// <returns>The superblock chroma transform-information span.</returns>
public Span < Av1TransformInfo > GetSuperblockTransformUv ( )
public Span < Av1TransformInfo > GetSuperblockTransformUv ( Point index )
= > this . transformInfoScratch . GetSpan ( ) . Slice (
{
this . modeInfoCountPerSuperblock ,
Span < Av1TransformInfo > span = this . transformInfosUv ;
this . modeInfoCountPerSuperblock < < 1 ) ;
int offset = ( ( ( index . Y * this . superblockColumnCount ) + index . X ) * this . modeInfoCountPerSuperblock ) < < 1 ;
return span . Slice ( offset , this . modeInfoCountPerSuperblock < < 1 ) ;
}
/// <summary>
/// <summary>
/// Gets the luma coefficient storage for a specified superblock.
/// Gets the luma coefficient scratch reused for the current superblock.
/// </summary>
/// </summary>
/// <param name="index">The position in the frame superblock grid.</param>
/// <returns>The current superblock luma coefficient span.</returns>
/// <returns>The superblock luma coefficient span.</returns>
public Span < int > GetCoefficientsY ( )
public Span < int > GetCoefficientsY ( Point index )
= > this . coefficientScratch . GetSpan ( ) [ . . this . lumaCoefficientCount ] ;
{
Span < int > span = this . coefficientsY ;
int count = this . modeInfoCountPerSuperblock * CoefficientCountPerModeInfo ;
int superblock = ( index . Y * this . superblockColumnCount ) + index . X ;
return span . Slice ( superblock * count , count ) ;
}
/// <summary>
/// <summary>
/// Gets the blue-difference chroma coefficient storage for a specified superblock.
/// Gets the blue-difference chroma coefficient scratch reused for the current superblock.
/// </summary>
/// </summary>
/// <param name="index">The position in the frame superblock grid.</param>
/// <returns>The current superblock blue-difference chroma coefficient span.</returns>
/// <returns>The superblock blue-difference chroma coefficient span.</returns>
public Span < int > GetCoefficientsU ( )
public Span < int > GetCoefficientsU ( Point index )
= > this . coefficientScratch . GetSpan ( ) . Slice ( this . lumaCoefficientCount , this . chromaCoefficientCount ) ;
{
Span < int > span = this . coefficientsU ;
int count = ( this . modeInfoCountPerSuperblock * CoefficientCountPerModeInfo ) > > this . subsamplingFactor ;
int superblock = ( index . Y * this . superblockColumnCount ) + index . X ;
return span . Slice ( superblock * count , count ) ;
}
/// <summary>
/// <summary>
/// Gets the red-difference chroma coefficient storage for a specified superblock.
/// Gets the red-difference chroma coefficient scratch reused for the current superblock.
/// </summary>
/// </summary>
/// <param name="index">The position in the frame superblock grid.</param>
/// <returns>The current superblock red-difference chroma coefficient span.</returns>
/// <returns>The superblock red-difference chroma coefficient span.</returns>
public Span < int > GetCoefficientsV ( )
public Span < int > GetCoefficientsV ( Point index )
= > this . coefficientScratch . GetSpan ( ) . Slice (
{
this . lumaCoefficientCount + this . chromaCoefficientCount ,
Span < int > span = this . coefficientsV ;
this . chromaCoefficientCount ) ;
int count = ( this . modeInfoCountPerSuperblock * CoefficientCountPerModeInfo ) > > this . subsamplingFactor ;
int superblock = ( index . Y * this . superblockColumnCount ) + index . X ;
return span . Slice ( superblock * count , count ) ;
}
/// <summary>
/// <summary>
/// Gets a reference to the active base quantizer index for a specified superblock.
/// Gets a reference to the active base quantizer index for a specified superblock.
@ -483,9 +704,8 @@ internal partial class Av1FrameInfo : IDisposable
/// <returns>A reference to the superblock base quantizer index.</returns>
/// <returns>A reference to the superblock base quantizer index.</returns>
public ref int GetQuantizerIndex ( Point index )
public ref int GetQuantizerIndex ( Point index )
{
{
Span < int > span = this . quantizerIndices ;
int storageRow = ( index . Y * this . superblockColumnCount ) + index . X ;
int i = ( index . Y * this . superblockColumnCount ) + index . X ;
return ref this . quantizerIndices [ 0 , storageRow ] ;
return ref span [ i ] ;
}
}
/// <summary>
/// <summary>
@ -495,9 +715,8 @@ internal partial class Av1FrameInfo : IDisposable
/// <returns>The superblock filter-strength span.</returns>
/// <returns>The superblock filter-strength span.</returns>
public Span < int > GetCdefStrength ( Point index )
public Span < int > GetCdefStrength ( Point index )
{
{
Span < int > span = this . cdefStrength ;
int storageRow = ( index . Y * this . superblockColumnCount ) + index . X ;
int i = ( ( index . Y * this . superblockColumnCount ) + index . X ) < < this . cdefStrengthFactorLog2 ;
return this . cdefStrength . DangerousGetRowSpan ( storageRow ) ;
return span . Slice ( i , 1 < < this . cdefStrengthFactorLog2 ) ;
}
}
/// <summary>
/// <summary>
@ -520,9 +739,8 @@ internal partial class Av1FrameInfo : IDisposable
/// <returns>The superblock loop-filter delta span.</returns>
/// <returns>The superblock loop-filter delta span.</returns>
public Span < int > GetDeltaLoopFilter ( Point index )
public Span < int > GetDeltaLoopFilter ( Point index )
{
{
Span < int > span = this . deltaLoopFilter ;
int storageRow = ( index . Y * this . superblockColumnCount ) + index . X ;
int i = ( ( index . Y * this . superblockColumnCount ) + index . X ) < < this . deltaLoopFactorLog2 ;
return this . deltaLoopFilter . DangerousGetRowSpan ( storageRow ) ;
return span . Slice ( i , 1 < < this . deltaLoopFactorLog2 ) ;
}
}
/// <summary>
/// <summary>
@ -546,10 +764,12 @@ internal partial class Av1FrameInfo : IDisposable
/// <param name="row">The restoration-unit row.</param>
/// <param name="row">The restoration-unit row.</param>
/// <param name="column">The restoration-unit column.</param>
/// <param name="column">The restoration-unit column.</param>
/// <returns>The decoded restoration-unit information.</returns>
/// <returns>The decoded restoration-unit information.</returns>
public Av1LoopRestorationUnit GetLoopRestorationUnit ( int plane , int row , int column )
public ref Av1LoopRestorationUnit GetLoopRestorationUnit ( int plane , int row , int column )
{
{
int index = ( row * this . loopRestorationUnitColumns [ plane ] ) + column ;
Buffer2D < Av1LoopRestorationUnit > units = this . loopRestorationUnits [ plane ]
return this . loopRestorationUnits [ plane ] [ index ] ;
? ? throw new InvalidOperationException ( "The selected AV1 plane has no loop-restoration units." ) ;
return ref units [ column , row ] ;
}
}
/// <summary>
/// <summary>
@ -560,10 +780,27 @@ internal partial class Av1FrameInfo : IDisposable
public void UpdateModeInfo ( Av1BlockModeInfo modeInfo , Av1SuperblockInfo superblockInfo )
public void UpdateModeInfo ( Av1BlockModeInfo modeInfo , Av1SuperblockInfo superblockInfo )
{
{
Point modeInfoPosition = this . GetModeInfoPosition ( superblockInfo . Position , modeInfo . PositionInSuperblock ) ;
Point modeInfoPosition = this . GetModeInfoPosition ( superblockInfo . Position , modeInfo . PositionInSuperblock ) ;
int storageRow = ( superblockInfo . Position . Y * this . superblockColumnCount ) + superblockInfo . Position . X ;
ref int modeInfoCount = ref this . modeInfoCounts [ 0 , storageRow ] ;
DebugGuard . MustBeLessThan ( modeInfoCount , this . modeInfoCountPerSuperblock , nameof ( modeInfoCount ) ) ;
int storageIndex = ( storageRow * this . modeInfoCountPerSuperblock ) + modeInfoCount ;
modeInfo . ModeInfoIndex = this . modeInfoMap . NextIndex ;
modeInfo . ModeInfoIndex = this . modeInfoMap . NextIndex ;
this . modeInfos [ this . modeInfoMap . NextIndex ] = modeInfo ;
this . GetModeInfoByStorageIndex ( storageIndex ) = modeInfo ;
modeInfoCount + + ;
this . UpdateRetainedMotionField ( modeInfo , modeInfoPosition ) ;
this . UpdateRetainedMotionField ( modeInfo , modeInfoPosition ) ;
this . modeInfoMap . Update ( modeInfoPosition , modeInfo . BlockSize ) ;
this . modeInfoMap . Update ( modeInfoPosition , modeInfo . BlockSize , storageIndex ) ;
}
/// <summary>
/// Gets a reference to one packed mode-information record across allocator segments.
/// </summary>
/// <param name="storageIndex">The packed frame-storage index.</param>
/// <returns>A reference to the selected mode information.</returns>
private ref Av1BlockModeInfo GetModeInfoByStorageIndex ( int storageIndex )
{
int bufferIndex = storageIndex / this . modeInfos . BufferLength ;
int elementIndex = storageIndex - ( bufferIndex * this . modeInfos . BufferLength ) ;
return ref this . modeInfos [ bufferIndex ] . Span [ elementIndex ] ;
}
}
/// <summary>
/// <summary>
@ -578,4 +815,85 @@ internal partial class Av1FrameInfo : IDisposable
int y = ( superblockPosition . Y * this . modeInfoSizePerSuperblock ) + positionInSuperblock . Y ;
int y = ( superblockPosition . Y * this . modeInfoSizePerSuperblock ) + positionInSuperblock . Y ;
return new Point ( x , y ) ;
return new Point ( x , y ) ;
}
}
/// <summary>
/// Provides indexed and reference-preserving traversal over one superblock's discontiguous mode information.
/// </summary>
public readonly struct ModeInfoCollection
{
private readonly Av1FrameInfo owner ;
private readonly int startIndex ;
/// <summary>
/// Initializes a new instance of the <see cref="ModeInfoCollection"/> struct.
/// </summary>
/// <param name="owner">The frame that owns the mode-information group.</param>
/// <param name="startIndex">The packed index of the first record.</param>
/// <param name="length">The number of records in the view.</param>
public ModeInfoCollection ( Av1FrameInfo owner , int startIndex , int length )
{
this . owner = owner ;
this . startIndex = startIndex ;
this . Length = length ;
}
/// <summary>
/// Gets the number of records in the view.
/// </summary>
public int Length { get ; }
/// <summary>
/// Gets a reference to the record at the specified traversal index.
/// </summary>
/// <param name="index">The zero-based traversal index.</param>
public ref Av1BlockModeInfo this [ int index ] = >
ref this . owner . GetModeInfoByStorageIndex ( this . startIndex + index ) ;
/// <summary>
/// Creates a reference-preserving enumerator over the records.
/// </summary>
/// <returns>The initialized enumerator.</returns>
public Enumerator GetEnumerator ( ) = > new ( this . owner , this . startIndex , this . Length ) ;
/// <summary>
/// Enumerates one superblock's mode-information records without flattening allocator segments.
/// </summary>
public struct Enumerator
{
private readonly Av1FrameInfo owner ;
private readonly int startIndex ;
private readonly int length ;
private int index ;
/// <summary>
/// Initializes a new instance of the <see cref="Enumerator"/> struct.
/// </summary>
/// <param name="owner">The frame that owns the mode-information group.</param>
/// <param name="startIndex">The packed index of the first record.</param>
/// <param name="length">The number of records in the view.</param>
public Enumerator ( Av1FrameInfo owner , int startIndex , int length )
{
this . owner = owner ;
this . startIndex = startIndex ;
this . length = length ;
this . index = - 1 ;
}
/// <summary>
/// Gets a reference to the current record.
/// </summary>
public ref Av1BlockModeInfo Current = >
ref this . owner . GetModeInfoByStorageIndex ( this . startIndex + this . index ) ;
/// <summary>
/// Advances to the next record.
/// </summary>
/// <returns><see langword="true"/> when another record is available.</returns>
public bool MoveNext ( )
{
this . index + + ;
return this . index < this . length ;
}
}
}
}
}