@ -2,16 +2,69 @@
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit ;
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction ;
using SixLabors.ImageSharp.Formats.Heif.Av1.Quantization ;
using SixLabors.ImageSharp.Formats.Heif.Av1.Symbol ;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform ;
namespace SixLabors.ImageSharp.Formats.Heif.Av1 ;
internal class Av1Decoder : IAv1TileDecoder
{
private static readonly int [ ] SgrprojXqdMid = [ - 3 2 , 3 1 ] ;
private static readonly int [ ] WienerTapsMid = [ 3 , - 7 , 1 5 ] ;
private const int PartitionProbabilitySet = 4 ;
private int [ ] deltaLoopFilter = [ ] ;
private bool [ ] [ ] [ ] blockDecoded = [ ] ;
private int [ ] [ ] referenceSgrXqd = [ ] ;
private int [ ] [ ] [ ] referenceLrWiener = [ ] ;
private bool availableUp ;
private bool availableLeft ;
private bool availableUpForChroma ;
private bool availableLeftForChroma ;
private Av1ParseAboveContext aboveContext = new ( ) ;
private Av1ParseLeftContext leftContext = new ( ) ;
private bool skip ;
private bool readDeltas ;
private int currentQuantizerIndex ;
private Av1PredictionMode [ ] [ ] YModes = [ ] ;
private Av1PredictionMode YMode = Av1PredictionMode . DC ;
private Av1PredictionMode UvMode = Av1PredictionMode . DC ;
private Av1PredictionMode [ ] [ ] UvModes = [ ] ;
private object [ ] ReferenceFrame = [ ] ;
private object [ ] [ ] [ ] ReferenceFrames = [ ] ;
private bool HasChroma ;
private bool SubsamplingX ;
private bool SubsamplingY ;
private int PaletteSizeY ;
private int PaletteSizeUv ;
private object [ ] [ ] aboveLevelContext = [ ] ;
private object [ ] [ ] aboveDcContext = [ ] ;
private object [ ] [ ] leftLevelContext = [ ] ;
private object [ ] [ ] leftDcContext = [ ] ;
private Av1TransformSize TransformSize = Av1TransformSize . Size4x4 ;
private bool skipMode ;
private bool IsChromaForLumaAllowed ;
private object FilterUltraMode = - 1 ;
private int AngleDeltaY ;
private int AngleDeltaUv ;
private bool Lossless ;
private int [ ] [ ] SegmentIds = [ ] ;
private int MaxLumaWidth ;
private int MaxLumaHeight ;
private int segmentId ;
private int [ ] [ ] cdefIndex = [ ] ;
private int deltaLoopFilterResolution ;
private int deltaQuantizerResolution ;
public Av1Decoder ( )
{
this . FrameInfo = new ObuFrameHeader ( ) ;
this . SequenceHeader = new ObuSequenceHeader ( ) ;
this . TileInfo = new ObuTileInfo ( ) ;
this . SeenFrameHeader = false ;
this . currentQuantizerIndex = - 1 ;
}
public bool SequenceHeaderDone { get ; set ; }
@ -32,9 +85,96 @@ internal class Av1Decoder : IAv1TileDecoder
ObuReader . Read ( ref reader , buffer . Length , this , false ) ;
}
public void DecodeTile ( int tileNum )
public void DecodeTile ( Span < byte > tileData , int tileNum )
{
// TODO: Implement
Av1SymbolDecoder reader = new ( tileData ) ;
int tileRowIndex = tileNum / this . TileInfo . TileColumnCount ;
int tileColumnIndex = tileNum % this . TileInfo . TileColumnCount ;
this . aboveContext . Clear ( ) ;
this . ClearLoopFilterDelta ( ) ;
int planesCount = this . SequenceHeader . ColorConfig . ChannelCount ;
this . referenceSgrXqd = new int [ planesCount ] [ ] ;
this . referenceLrWiener = new int [ planesCount ] [ ] [ ] ;
for ( int plane = 0 ; plane < planesCount ; plane + + )
{
this . referenceSgrXqd [ plane ] = new int [ 2 ] ;
Array . Copy ( SgrprojXqdMid , this . referenceSgrXqd [ plane ] , SgrprojXqdMid . Length ) ;
this . referenceLrWiener [ plane ] = new int [ 2 ] [ ] ;
for ( int pass = 0 ; pass < 2 ; pass + + )
{
this . referenceLrWiener [ plane ] [ pass ] = new int [ ObuConstants . WienerCoefficientCount ] ;
Array . Copy ( WienerTapsMid , this . referenceLrWiener [ plane ] [ pass ] , WienerTapsMid . Length ) ;
}
}
Av1BlockSize superBlockSize = this . SequenceHeader . Use128x128SuperBlock ? Av1BlockSize . Block128x128 : Av1BlockSize . Block64x64 ;
int superBlock4x4Size = superBlockSize . Get4x4WideCount ( ) ;
for ( int row = this . TileInfo . TileRowStartModeInfo [ tileRowIndex ] ; row < this . TileInfo . TileRowStartModeInfo [ tileRowIndex + 1 ] ; row + = this . SequenceHeader . ModeInfoSize )
{
int superBlockRow = ( row < < ObuConstants . ModeInfoSizeLog2 ) > > this . SequenceHeader . SuperBlockSizeLog2 ;
this . leftContext . Clear ( ) ;
for ( int column = this . TileInfo . TileColumnStartModeInfo [ tileColumnIndex ] ; column < this . TileInfo . TileColumnStartModeInfo [ tileColumnIndex + 1 ] ; column + = this . SequenceHeader . ModeInfoSize )
{
int superBlockColumn = ( column < < ObuConstants . ModeInfoSizeLog2 ) > > this . SequenceHeader . SuperBlockSizeLog2 ;
bool subSamplingX = this . SequenceHeader . ColorConfig . SubSamplingX ;
bool subSamplingY = this . SequenceHeader . ColorConfig . SubSamplingY ;
bool ReadDeltas = this . FrameInfo . DeltaQParameters . IsPresent ;
// Nothing to do for CDEF
// this.ClearCdef(row, column);
this . ClearBlockDecodedFlags ( row , column , superBlock4x4Size ) ;
this . ReadLoopRestoration ( row , column , superBlockSize ) ;
this . DecodePartition ( ref reader , row , column , superBlockSize ) ;
}
}
}
private void ClearLoopFilterDelta ( ) = > this . deltaLoopFilter = new int [ 4 ] ;
private void ClearBlockDecodedFlags ( int row , int column , int superBlock4x4Size )
{
int planesCount = this . SequenceHeader . ColorConfig . ChannelCount ;
for ( int plane = 0 ; plane < planesCount ; plane + + )
{
int subX = ( plane > 0 & & this . SequenceHeader . ColorConfig . SubSamplingX ) ? 1 : 0 ;
int subY = ( plane > 0 & & this . SequenceHeader . ColorConfig . SubSamplingY ) ? 1 : 0 ;
int superBlock4x4Width = ( this . FrameInfo . ModeInfoColumnCount - column ) > > subX ;
int superBlock4x4Height = ( this . FrameInfo . ModeInfoRowCount - row ) > > subY ;
for ( int y = - 1 ; y < = ( superBlock4x4Size > > subY ) ; y + + )
{
for ( int x = - 1 ; x < = ( superBlock4x4Size > > subX ) ; x + + )
{
if ( y < 0 & & x < superBlock4x4Width )
{
this . blockDecoded [ plane ] [ y ] [ x ] = true ;
}
else if ( x < 0 & & y < superBlock4x4Height )
{
this . blockDecoded [ plane ] [ y ] [ x ] = true ;
}
else
{
this . blockDecoded [ plane ] [ y ] [ x ] = false ;
}
}
}
this . blockDecoded [ plane ] [ superBlock4x4Size > > subY ] [ - 1 ] = false ;
}
}
private void ReadLoopRestoration ( int row , int column , Av1BlockSize superBlockSize )
{
int planesCount = this . SequenceHeader . ColorConfig . ChannelCount ;
for ( int plane = 0 ; plane < planesCount ; plane + + )
{
if ( this . FrameInfo . LoopRestorationParameters [ plane ] . Type ! = ObuRestorationType . None )
{
// TODO: Implement.
throw new NotImplementedException ( "No loop restoration filter support." ) ;
}
}
}
public void FinishDecodeTiles ( bool doCdef , bool doLoopRestoration )
@ -42,9 +182,655 @@ internal class Av1Decoder : IAv1TileDecoder
// TODO: Implement
}
private static void DecodeBlock ( Av1BlockModeInfo blockMode , int rowIndex , int columnIndex )
private void DecodePartition ( ref Av1SymbolDecoder reader , int rowIndex , int columnIndex , Av1BlockSize blockSize )
{
if ( rowIndex > = this . TileInfo . TileRowStartModeInfo [ rowIndex ] | |
columnIndex > = this . TileInfo . TileColumnStartModeInfo [ columnIndex ] )
{
return ;
}
this . availableUp = this . IsInside ( rowIndex - 1 , columnIndex ) ;
this . availableLeft = this . IsInside ( rowIndex , columnIndex - 1 ) ;
int block4x4Size = blockSize . Get4x4WideCount ( ) ;
int halfBlock4x4Size = block4x4Size > > 1 ;
int quarterBlock4x4Size = halfBlock4x4Size > > 2 ;
bool hasRows = ( rowIndex + halfBlock4x4Size ) < this . TileInfo . TileRowCount ;
bool hasColumns = ( columnIndex + halfBlock4x4Size ) < this . TileInfo . TileColumnCount ;
Av1PartitionType partitionType = Av1PartitionType . Split ;
if ( blockSize < Av1BlockSize . Block8x8 )
{
partitionType = Av1PartitionType . None ;
}
else if ( hasRows & & hasColumns )
{
int ctx = this . GetPartitionContext ( rowIndex , columnIndex , blockSize ) ;
partitionType = reader . ReadPartitionSymbol ( ctx ) ;
}
else if ( hasColumns )
{
int ctx = this . GetPartitionContext ( rowIndex , columnIndex , blockSize ) ;
bool splitOrHorizontal = reader . ReadSplitOrHorizontal ( blockSize , ctx ) ;
partitionType = splitOrHorizontal ? Av1PartitionType . Split : Av1PartitionType . Horizontal ;
}
else if ( hasRows )
{
int ctx = this . GetPartitionContext ( rowIndex , columnIndex , blockSize ) ;
bool splitOrVertical = reader . ReadSplitOrVertical ( blockSize , ctx ) ;
partitionType = splitOrVertical ? Av1PartitionType . Split : Av1PartitionType . Vertical ;
}
Av1BlockSize subSize = partitionType . GetBlockSubSize ( blockSize ) ;
Av1BlockSize splitSize = Av1PartitionType . Split . GetBlockSubSize ( blockSize ) ;
switch ( partitionType )
{
case Av1PartitionType . Split :
this . DecodePartition ( ref reader , rowIndex , columnIndex , subSize ) ;
this . DecodePartition ( ref reader , rowIndex , columnIndex + halfBlock4x4Size , subSize ) ;
this . DecodePartition ( ref reader , rowIndex + halfBlock4x4Size , columnIndex , subSize ) ;
this . DecodePartition ( ref reader , rowIndex + halfBlock4x4Size , columnIndex + halfBlock4x4Size , subSize ) ;
break ;
case Av1PartitionType . None :
this . DecodeBlock ( ref reader , rowIndex , columnIndex , subSize ) ;
break ;
default :
throw new NotImplementedException ( $"Partition type: {partitionType} is not supported." ) ;
}
}
private void DecodeBlock ( ref Av1SymbolDecoder reader , int rowIndex , int columnIndex , Av1BlockSize blockSize )
{
int block4x4Width = blockSize . Get4x4WideCount ( ) ;
int block4x4Height = blockSize . Get4x4HighCount ( ) ;
int planesCount = this . SequenceHeader . ColorConfig . ChannelCount ;
bool hasChroma = planesCount > 1 ;
if ( block4x4Height = = 1 & & this . SequenceHeader . ColorConfig . SubSamplingY & & ( rowIndex & 0x1 ) = = 0 )
{
hasChroma = false ;
}
if ( block4x4Width = = 1 & & this . SequenceHeader . ColorConfig . SubSamplingX & & ( columnIndex & 0x1 ) = = 0 )
{
hasChroma = false ;
}
this . availableUp = this . IsInside ( rowIndex - 1 , columnIndex ) ;
this . availableLeft = this . IsInside ( rowIndex , columnIndex - 1 ) ;
this . availableUpForChroma = this . availableUp ;
this . availableLeftForChroma = this . availableLeft ;
if ( hasChroma )
{
if ( this . SequenceHeader . ColorConfig . SubSamplingY & & block4x4Height = = 1 )
{
this . availableUpForChroma = this . IsInside ( rowIndex - 2 , columnIndex ) ;
}
if ( this . SequenceHeader . ColorConfig . SubSamplingX & & block4x4Width = = 1 )
{
this . availableLeftForChroma = this . IsInside ( rowIndex , columnIndex - 2 ) ;
}
}
this . ReadModeInfo ( ref reader , rowIndex , columnIndex , blockSize ) ;
this . ReadPaletteTokens ( ref reader ) ;
this . ReadBlockTransformSize ( ref reader , rowIndex , columnIndex , blockSize ) ;
if ( this . skip )
{
this . ResetBlockContext ( rowIndex , columnIndex , block4x4Width , block4x4Height ) ;
}
bool isCompound = false ;
for ( int y = 0 ; y < block4x4Height ; y + + )
{
for ( int x = 0 ; x < block4x4Width ; x + + )
{
this . YModes [ rowIndex + y ] [ columnIndex + x ] = this . YMode ;
if ( this . ReferenceFrame [ 0 ] = = ( object ) ObuFrameType . IntraOnlyFrame & & hasChroma )
{
this . UvModes [ rowIndex + y ] [ columnIndex + x ] = this . UvMode ;
}
for ( int refList = 0 ; refList < 2 ; refList + + )
{
this . ReferenceFrames [ rowIndex + y ] [ columnIndex + x ] [ refList ] = this . ReferenceFrame [ refList ] ;
}
}
}
this . ComputePrediction ( ) ;
this . Residual ( rowIndex , columnIndex , blockSize ) ;
}
private void Residual ( int rowIndex , int columnIndex , Av1BlockSize blockSize )
{
int superBlockMask = this . SequenceHeader . Use128x128SuperBlock ? 3 1 : 1 5 ;
int widthChunks = Math . Max ( 1 , blockSize . Get4x4WideCount ( ) > > 6 ) ;
int heightChunks = Math . Max ( 1 , blockSize . Get4x4HighCount ( ) > > 6 ) ;
Av1BlockSize sizeChunk = ( widthChunks > 1 | | heightChunks > 1 ) ? Av1BlockSize . Block64x64 : blockSize ;
for ( int chunkY = 0 ; chunkY < heightChunks ; chunkY + + )
{
for ( int chunkX = 0 ; chunkX < widthChunks ; chunkX + + )
{
int rowChunk = rowIndex + ( chunkY < < 4 ) ;
int columnChunk = columnIndex + ( chunkX < < 4 ) ;
int subBlockRow = rowChunk & superBlockMask ;
int subBlockColumn = columnChunk & superBlockMask ;
for ( int plane = 0 ; plane < 1 + ( this . HasChroma ? 2 : 0 ) ; plane + + )
{
Av1TransformSize transformSize = this . FrameInfo . CodedLossless ? Av1TransformSize . Size4x4 : this . GetSize ( plane , this . TransformSize ) ;
int stepX = transformSize . GetWidth ( ) > > 2 ;
int stepY = transformSize . GetHeight ( ) > > 2 ;
Av1BlockSize planeSize = this . GetPlaneResidualSize ( sizeChunk , plane ) ;
int num4x4Width = planeSize . Get4x4WideCount ( ) ;
int num4x4Height = planeSize . Get4x4HighCount ( ) ;
int subX = ( plane > 0 & & this . SubsamplingX ) ? 1 : 0 ;
int subY = ( plane > 0 & & this . SubsamplingY ) ? 1 : 0 ;
int baseX = ( columnChunk > > subX ) * ( 1 < < ObuConstants . ModeInfoSizeLog2 ) ;
int baseY = ( rowChunk > > subY ) * ( 1 < < ObuConstants . ModeInfoSizeLog2 ) ;
int baseXBlock = ( columnIndex > > subX ) * ( 1 < < ObuConstants . ModeInfoSizeLog2 ) ;
int baseYBlock = ( rowIndex > > subY ) * ( 1 < < ObuConstants . ModeInfoSizeLog2 ) ;
for ( int y = 0 ; y < num4x4Height ; y + = stepY )
{
for ( int x = 0 ; x < num4x4Width ; x + = stepX )
{
this . TransformBlock ( plane , baseXBlock , baseYBlock , transformSize , x + ( ( chunkX < < 4 ) > > subX ) , y + ( ( chunkY < < 4 ) > > subY ) ) ;
}
}
}
}
}
}
private Av1TransformSize GetSize ( int plane , object transformSize ) = > throw new NotImplementedException ( ) ;
private Av1BlockSize GetPlaneResidualSize ( Av1BlockSize sizeChunk , int plane ) = > throw new NotImplementedException ( ) ;
private void TransformBlock ( int plane , int baseX , int baseY , Av1TransformSize transformSize , int x , int y )
{
int startX = baseX + ( 4 * x ) ;
int startY = baseY + ( 4 * y ) ;
int subX = ( plane > 0 & & this . SubsamplingX ) ? 1 : 0 ;
int subY = ( plane > 0 & & this . SubsamplingY ) ? 1 : 0 ;
int columnIndex = ( startX < < subX ) > > ObuConstants . ModeInfoSizeLog2 ;
int rowIndex = ( startY < < subY ) > > ObuConstants . ModeInfoSizeLog2 ;
int superBlockMask = this . SequenceHeader . Use128x128SuperBlock ? 3 1 : 1 5 ;
int subBlockColumn = columnIndex & superBlockMask ;
int subBlockRow = rowIndex & superBlockMask ;
int stepX = transformSize . GetWidth ( ) > > ObuConstants . ModeInfoSizeLog2 ;
int stepY = transformSize . GetHeight ( ) > > ObuConstants . ModeInfoSizeLog2 ;
int maxX = ( this . SequenceHeader . ModeInfoSize * ( 1 < < ObuConstants . ModeInfoSizeLog2 ) ) > > subX ;
int maxY = ( this . SequenceHeader . ModeInfoSize * ( 1 < < ObuConstants . ModeInfoSizeLog2 ) ) > > subY ;
if ( startX > = maxX | | startY > = maxY )
{
return ;
}
if ( ( plane = = 0 & & this . PaletteSizeY > 0 ) | |
( plane ! = 0 & & this . PaletteSizeUv > 0 ) )
{
this . PredictPalette ( plane , startX , startY , x , y , transformSize ) ;
}
else
{
bool isChromaFromLuma = plane > 0 & & this . UvMode = = Av1PredictionMode . UvChromaFromLuma ;
Av1PredictionMode mode ;
if ( plane = = 0 )
{
mode = this . YMode ;
}
else
{
mode = isChromaFromLuma ? Av1PredictionMode . DC : this . UvMode ;
}
int log2Width = transformSize . GetWidthLog2 ( ) ;
int log2Height = transformSize . GetHeightLog2 ( ) ;
bool leftAvailable = ( x > 0 ) | | plane = = 0 ? this . availableLeft : this . availableLeftForChroma ;
bool upAvailable = ( y > 0 ) | | plane = = 0 ? this . availableUp : this . availableUpForChroma ;
bool haveAboveRight = this . blockDecoded [ plane ] [ ( subBlockRow > > subY ) - 1 ] [ ( subBlockColumn > > subX ) + stepX ] ;
bool haveBelowLeft = this . blockDecoded [ plane ] [ ( subBlockRow > > subY ) + stepY ] [ ( subBlockColumn > > subX ) - 1 ] ;
this . PredictIntra ( plane , startX , startY , leftAvailable , upAvailable , haveAboveRight , haveBelowLeft , mode , log2Width , log2Height ) ;
if ( isChromaFromLuma )
{
this . PredictChromaFromLuma ( plane , startX , startY , transformSize ) ;
}
}
if ( plane = = 0 )
{
this . MaxLumaWidth = startX + ( stepX * 4 ) ;
this . MaxLumaHeight = startY + ( stepY * 4 ) ;
}
if ( ! this . skip )
{
int eob = this . Coefficients ( plane , startX , startY , transformSize ) ;
if ( eob > 0 )
{
this . Reconstruct ( plane , startX , startY , transformSize ) ;
}
}
for ( int i = 0 ; i < stepY ; i + + )
{
for ( int j = 0 ; j < stepX ; j + + )
{
// Ignore loop filter.
this . blockDecoded [ plane ] [ ( subBlockRow > > subY ) + i ] [ ( subBlockColumn > > subX ) + j ] = true ;
}
}
}
private void Reconstruct ( int plane , int startX , int startY , Av1TransformSize transformSize ) = > throw new NotImplementedException ( ) ;
private int Coefficients ( int plane , int startX , int startY , Av1TransformSize transformSize ) = > throw new NotImplementedException ( ) ;
private void PredictChromaFromLuma ( int plane , int startX , int startY , Av1TransformSize transformSize ) = > throw new NotImplementedException ( ) ;
private void PredictIntra ( int plane , int startX , int startY , bool leftAvailable , bool upAvailable , bool haveAboveRight , bool haveBelowLeft , Av1PredictionMode mode , int log2Width , int log2Height ) = > throw new NotImplementedException ( ) ;
private void PredictPalette ( int plane , int startX , int startY , int x , int y , Av1TransformSize transformSize ) = > throw new NotImplementedException ( ) ;
private void ComputePrediction ( )
{
// Not applicable for INTRA frames.
}
private void ResetBlockContext ( int rowIndex , int columnIndex , int block4x4Width , int block4x4Height )
{
for ( int plane = 0 ; plane < 1 + ( this . HasChroma ? 2 : 0 ) ; plane + + )
{
int subX = ( plane > 0 & & this . SubsamplingX ) ? 1 : 0 ;
int subY = ( plane > 0 & & this . SubsamplingY ) ? 1 : 0 ;
for ( int i = columnIndex > > subX ; i < ( ( columnIndex + block4x4Width ) > > subX ) ; i + + )
{
this . aboveLevelContext [ plane ] [ i ] = 0 ;
this . aboveDcContext [ plane ] [ i ] = 0 ;
}
for ( int i = rowIndex > > subY ; i < ( ( rowIndex + block4x4Height ) > > subY ) ; i + + )
{
this . leftLevelContext [ plane ] [ i ] = 0 ;
this . leftDcContext [ plane ] [ i ] = 0 ;
}
}
}
private void ReadBlockTransformSize ( ref Av1SymbolDecoder reader , int rowIndex , int columnIndex , Av1BlockSize blockSize )
{
int block4x4Width = blockSize . Get4x4WideCount ( ) ;
int block4x4Height = blockSize . Get4x4HighCount ( ) ;
// First condition in spec is for INTER frames, implemented only the INTRA condition.
this . ReadBlockTransformSize ( ref reader , rowIndex , columnIndex , blockSize ) ;
/ * for ( int row = rowIndex ; row < rowIndex + block4x4Height ; row + + )
{
for ( int column = columnIndex ; column < columnIndex + block4x4Width ; column + + )
{
this . InterTransformSizes [ row ] [ column ] = this . TransformSize ;
}
} * /
}
private void ReadPaletteTokens ( ref Av1SymbolDecoder reader )
{
if ( this . PaletteSizeY ! = 0 )
{
// Todo: Implement.
throw new NotImplementedException ( ) ;
}
if ( this . PaletteSizeUv ! = 0 )
{
// Todo: Implement.
throw new NotImplementedException ( ) ;
}
}
private void ReadModeInfo ( ref Av1SymbolDecoder reader , int rowIndex , int columnIndex , Av1BlockSize blockSize )
= > this . ReadIntraFrameModeInfo ( ref reader , rowIndex , columnIndex , blockSize ) ;
private void ReadIntraFrameModeInfo ( ref Av1SymbolDecoder reader , int rowIndex , int columnIndex , Av1BlockSize blockSize )
{
this . skip = false ;
if ( this . FrameInfo . SegmentationParameters . SegmentIdPrecedesSkip )
{
this . ReadIntraSegmentId ( ref reader ) ;
}
this . skipMode = false ;
this . ReadSkip ( ref reader ) ;
if ( ! this . FrameInfo . SegmentationParameters . SegmentIdPrecedesSkip )
{
this . IntraSegmentId ( ref reader , rowIndex , columnIndex ) ;
}
this . ReadCdef ( ref reader , rowIndex , columnIndex , blockSize ) ;
this . ReadDeltaQuantizerIndex ( ref reader , blockSize ) ;
this . ReadDeltaLoopFilter ( ref reader , blockSize ) ;
this . readDeltas = false ;
this . ReferenceFrame [ 0 ] = - 1 ; // IntraFrame;
this . ReferenceFrame [ 1 ] = - 1 ; // None;
bool useIntraBlockCopy = false ;
if ( this . FrameInfo . AllowIntraBlockCopy )
{
useIntraBlockCopy = reader . ReadUseIntraBlockCopy ( ) ;
}
if ( useIntraBlockCopy )
{
// TODO: Implement
}
else
{
// this.IsInter = false;
this . YMode = reader . ReadIntraFrameYMode ( blockSize ) ;
this . IntraAngleInfoY ( ref reader , blockSize ) ;
if ( this . HasChroma )
{
this . UvMode = reader . ReadUvMode ( blockSize , this . IsChromaForLumaAllowed ) ;
if ( this . UvMode = = Av1PredictionMode . UvChromaFromLuma )
{
this . ReadChromaFromLumaAlphas ( ref reader ) ;
}
this . IntraAngleInfoUv ( ref reader , blockSize ) ;
}
this . PaletteSizeY = 0 ;
this . PaletteSizeUv = 0 ;
if ( this . SequenceHeader . ModeInfoSize > = ( int ) Av1BlockSize . Block8x8 & &
( ( Av1BlockSize ) this . SequenceHeader . ModeInfoSize ) . Get4x4WideCount ( ) < = 6 4 & &
( ( Av1BlockSize ) this . SequenceHeader . ModeInfoSize ) . Get4x4HighCount ( ) < = 6 4 & &
this . FrameInfo . AllowScreenContentTools )
{
this . PaletteModeInfo ( ref reader ) ;
}
this . FilterIntraModeInfo ( ref reader , blockSize ) ;
}
}
private void ReadIntraSegmentId ( ref Av1SymbolDecoder reader ) = > throw new NotImplementedException ( ) ;
private void FilterIntraModeInfo ( ref Av1SymbolDecoder reader , Av1BlockSize blockSize )
{
bool useFilterIntra = false ;
if ( this . SequenceHeader . EnableFilterIntra & &
this . YMode = = Av1PredictionMode . DC & & this . PaletteSizeY = = 0 & &
Math . Max ( blockSize . GetWidth ( ) , blockSize . GetHeight ( ) ) < = 3 2 )
{
useFilterIntra = reader . ReadUseFilterUltra ( ) ;
if ( useFilterIntra )
{
this . FilterUltraMode = reader . ReadFilterUltraMode ( ) ;
}
}
}
private void PaletteModeInfo ( ref Av1SymbolDecoder reader ) = >
// TODO: Implement.
throw new NotImplementedException ( ) ;
private void ReadChromaFromLumaAlphas ( ref Av1SymbolDecoder reader ) = >
// TODO: Implement.
throw new NotImplementedException ( ) ;
private void IntraAngleInfoY ( ref Av1SymbolDecoder reader , Av1BlockSize blockSize )
{
this . AngleDeltaY = 0 ;
if ( blockSize > = Av1BlockSize . Block8x8 & & IsDirectionalMode ( this . YMode ) )
{
int angleDeltaY = reader . ReadAngleDelta ( this . YMode ) ;
this . AngleDeltaY = angleDeltaY - ObuConstants . MaxAngleDelta ;
}
}
private void IntraAngleInfoUv ( ref Av1SymbolDecoder reader , Av1BlockSize blockSize )
{
this . AngleDeltaUv = 0 ;
if ( blockSize > = Av1BlockSize . Block8x8 & & IsDirectionalMode ( this . UvMode ) )
{
int angleDeltaUv = reader . ReadAngleDelta ( this . UvMode ) ;
this . AngleDeltaUv = angleDeltaUv - ObuConstants . MaxAngleDelta ;
}
}
private static bool IsDirectionalMode ( Av1PredictionMode mode )
= > mode is > = Av1PredictionMode . Vertical and < = Av1PredictionMode . Directional67Degrees ;
private void IntraSegmentId ( ref Av1SymbolDecoder reader , int rowIndex , int columnIndex )
{
if ( this . FrameInfo . SegmentationParameters . Enabled )
{
this . ReadSegmentId ( ref reader , rowIndex , columnIndex ) ;
}
else
{
this . segmentId = 0 ;
}
this . Lossless = this . FrameInfo . LosslessArray [ this . segmentId ] ;
}
private void ReadSegmentId ( ref Av1SymbolDecoder reader , int rowIndex , int columnIndex )
{
int pred ;
int prevUL = - 1 ;
int prevU = - 1 ;
int prevL = - 1 ;
if ( this . availableUp & & this . availableLeft )
{
prevUL = this . SegmentIds [ rowIndex - 1 ] [ columnIndex - 1 ] ;
}
if ( this . availableUp )
{
prevU = this . SegmentIds [ rowIndex - 1 ] [ columnIndex ] ;
}
if ( this . availableLeft )
{
prevU = this . SegmentIds [ rowIndex ] [ columnIndex - 1 ] ;
}
if ( prevU = = - 1 )
{
pred = ( prevL = = - 1 ) ? 0 : prevL ;
}
else if ( prevL = = - 1 )
{
pred = prevU ;
}
else
{
pred = ( prevU = = prevUL ) ? prevU : prevL ;
}
if ( this . skip )
{
this . segmentId = 0 ;
}
else
{
int lastActiveSegmentId = this . FrameInfo . SegmentationParameters . LastActiveSegmentId ;
this . segmentId = NegativeDeinterleave ( reader . ReadSegmentId ( - 1 ) , pred , lastActiveSegmentId + 1 ) ;
}
}
private static int NegativeDeinterleave ( int diff , int reference , int max )
{
if ( reference = = 0 )
{
return diff ;
}
if ( reference > = ( max - 1 ) )
{
return max - diff - 1 ;
}
if ( 2 * reference < max )
{
if ( diff < = 2 * reference )
{
if ( ( diff & 1 ) > 0 )
{
return reference + ( ( diff + 1 ) > > 1 ) ;
}
else
{
return reference - ( diff > > 1 ) ;
}
}
return diff ;
}
else
{
if ( diff < = 2 * ( max - reference - 1 ) )
{
if ( ( diff & 1 ) > 0 )
{
return reference + ( ( diff + 1 ) > > 1 ) ;
}
else
{
return reference - ( diff > > 1 ) ;
}
}
return max - ( diff + 1 ) ;
}
}
private void ReadCdef ( ref Av1SymbolDecoder reader , int rowIndex , int columnIndex , Av1BlockSize blockSize )
{
if ( this . skip | | this . FrameInfo . CodedLossless | | ! this . SequenceHeader . EnableCdef | | this . FrameInfo . AllowIntraBlockCopy )
{
return ;
}
int cdefSize4 = Av1BlockSize . Block64x64 . Get4x4WideCount ( ) ;
int cdefMask4 = ~ ( cdefSize4 - 1 ) ;
int r = rowIndex & cdefMask4 ;
int c = columnIndex & cdefMask4 ;
if ( this . cdefIndex [ r ] [ c ] = = - 1 )
{
this . cdefIndex [ r ] [ c ] = reader . ReadLiteral ( this . FrameInfo . CdefParameters . BitCount ) ;
int w4 = blockSize . Get4x4WideCount ( ) ;
int h4 = blockSize . Get4x4HighCount ( ) ;
for ( int i = r ; i < r + h4 ; i + = cdefSize4 )
{
for ( int j = c ; j < c + w4 ; j + = cdefSize4 )
{
this . cdefIndex [ i ] [ j ] = this . cdefIndex [ r ] [ c ] ;
}
}
}
}
private void ReadDeltaLoopFilter ( ref Av1SymbolDecoder reader , Av1BlockSize blockSize )
{
Av1BlockSize superBlockSize = this . SequenceHeader . Use128x128SuperBlock ? Av1BlockSize . Block128x128 : Av1BlockSize . Block64x64 ;
if ( blockSize = = superBlockSize & & this . skip )
{
return ;
}
if ( this . readDeltas & & this . FrameInfo . DeltaLoopFilterParameters . IsPresent )
{
int frameLoopFilterCount = 1 ;
if ( this . FrameInfo . DeltaLoopFilterParameters . Multi )
{
frameLoopFilterCount = ( this . SequenceHeader . ColorConfig . ChannelCount > 1 ) ? ObuConstants . FrameLoopFilterCount : ObuConstants . FrameLoopFilterCount - 2 ;
}
for ( int i = 0 ; i < frameLoopFilterCount ; i + + )
{
int deltaLoopFilterAbsolute = reader . ReadDeltaLoopFilterAbsolute ( ) ;
if ( deltaLoopFilterAbsolute = = ObuConstants . DeltaLoopFilterSmall )
{
int deltaLoopFilterRemainingBits = reader . ReadLiteral ( 3 ) + 1 ;
int deltaLoopFilterAbsoluteBitCount = reader . ReadLiteral ( deltaLoopFilterRemainingBits ) ;
deltaLoopFilterAbsolute = deltaLoopFilterAbsoluteBitCount + ( 1 < < deltaLoopFilterRemainingBits ) + 1 ;
}
if ( deltaLoopFilterAbsolute ! = 0 )
{
bool deltaLoopFilterSign = reader . ReadLiteral ( 1 ) > 0 ;
int reducedDeltaLoopFilterLevel = deltaLoopFilterSign ? - deltaLoopFilterAbsolute : deltaLoopFilterAbsolute ;
this . deltaLoopFilter [ i ] = Av1Math . Clip3 ( - ObuConstants . MaxLoopFilter , ObuConstants . MaxLoopFilter , this . deltaLoopFilter [ i ] + ( reducedDeltaLoopFilterLevel < < this . deltaLoopFilterResolution ) ) ;
}
}
}
}
private void ReadSkip ( ref Av1SymbolDecoder reader )
{
if ( this . FrameInfo . SegmentationParameters . SegmentIdPrecedesSkip & & this . FrameInfo . SegmentationParameters . IsFeatureActive ( ObuSegmentationFeature . LevelSkip ) )
{
this . skip = true ;
}
else
{
this . skip = reader . ReadSkip ( - 1 ) ;
}
}
private void ReadDeltaQuantizerIndex ( ref Av1SymbolDecoder reader , Av1BlockSize blockSize )
{
Av1BlockSize superBlockSize = this . SequenceHeader . Use128x128SuperBlock ? Av1BlockSize . Block128x128 : Av1BlockSize . Block64x64 ;
if ( blockSize = = superBlockSize & & this . skip )
{
return ;
}
if ( this . readDeltas )
{
int deltaQuantizerAbsolute = reader . ReadDeltaQuantizerAbsolute ( ) ;
if ( deltaQuantizerAbsolute = = ObuConstants . DeltaQuantizerSmall )
{
int deltaQuantizerRemainingBits = reader . ReadLiteral ( 3 ) + 1 ;
int deltaQuantizerAbsoluteBitCount = reader . ReadLiteral ( deltaQuantizerRemainingBits ) ;
deltaQuantizerAbsolute = deltaQuantizerRemainingBits + ( 1 < < deltaQuantizerRemainingBits ) + 1 ;
}
if ( deltaQuantizerAbsolute ! = 0 )
{
bool deltaQuantizerSignBit = reader . ReadLiteral ( 1 ) > 0 ;
int reducedDeltaQuantizerIndex = deltaQuantizerSignBit ? - deltaQuantizerAbsolute : deltaQuantizerAbsolute ;
this . currentQuantizerIndex = Av1Math . Clip3 ( 1 , 2 5 5 , this . currentQuantizerIndex + ( reducedDeltaQuantizerIndex < < this . deltaQuantizerResolution ) ) ;
}
}
}
private bool IsInside ( int rowIndex , int columnIndex ) = >
columnIndex > = this . TileInfo . TileColumnCount & &
columnIndex < this . TileInfo . TileColumnCount & &
rowIndex > = this . TileInfo . TileRowCount & &
rowIndex < this . TileInfo . TileRowCount ;
private static bool IsChroma ( int rowIndex , int columnIndex , Av1BlockModeInfo blockMode , bool subSamplingX , bool subSamplingY )
{
int block4x4Width = blockMode . BlockSize . Get4x4WideCount ( ) ;
int block4x4Height = blockMode . BlockSize . Get4x4HighCount ( ) ;
bool xPos = ( columnIndex & 0x1 ) > 0 | | ( block4x4Width & 0x1 ) > 0 | | ! subSamplingX ;
bool yPos = ( rowIndex & 0x1 ) > 0 | | ( block4x4Height & 0x1 ) > 0 | | ! subSamplingY ;
return xPos & & yPos ;
}
private int GetPartitionContext ( int rowIndex , int columnIndex , Av1BlockSize blockSize )
{
// Maximum partition point is 8x8. Offset the log value occordingly.
int blockSizeLog = blockSize . Get4x4WidthLog2 ( ) - Av1BlockSize . Block8x8 . Get4x4WidthLog2 ( ) ;
int aboveCtx = this . aboveContext . PartitionWidth + columnIndex - this . TileInfo . TileColumnStartModeInfo [ columnIndex ] ;
int leftCtx = this . leftContext . PartitionHeight + rowIndex - this . TileInfo . TileRowStartModeInfo [ rowIndex ] ;
int above = ( aboveCtx > > blockSizeLog ) & 0x1 ;
int left = ( leftCtx > > blockSizeLog ) & 0x1 ;
return ( ( left * 2 ) + above ) + ( blockSizeLog * PartitionProbabilitySet ) ;
}
}