|
|
@ -18,37 +18,16 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
private bool[][][] blockDecoded = []; |
|
|
private bool[][][] blockDecoded = []; |
|
|
private int[][] referenceSgrXqd = []; |
|
|
private int[][] referenceSgrXqd = []; |
|
|
private int[][][] referenceLrWiener = []; |
|
|
private int[][][] referenceLrWiener = []; |
|
|
private bool availableUp; |
|
|
|
|
|
private bool availableLeft; |
|
|
|
|
|
private bool availableUpForChroma; |
|
|
|
|
|
private bool availableLeftForChroma; |
|
|
|
|
|
private Av1ParseAboveContext aboveContext = new(); |
|
|
private Av1ParseAboveContext aboveContext = new(); |
|
|
private Av1ParseLeftContext leftContext = new(); |
|
|
private Av1ParseLeftContext leftContext = new(); |
|
|
private bool skip; |
|
|
|
|
|
private bool readDeltas; |
|
|
|
|
|
private int currentQuantizerIndex; |
|
|
private int currentQuantizerIndex; |
|
|
private Av1PredictionMode[][] yModes = []; |
|
|
private int[][] aboveLevelContext = []; |
|
|
private Av1PredictionMode yMode = Av1PredictionMode.DC; |
|
|
private int[][] aboveDcContext = []; |
|
|
private Av1PredictionMode uvMode = Av1PredictionMode.DC; |
|
|
private int[][] leftLevelContext = []; |
|
|
private Av1PredictionMode[][] uvModes = []; |
|
|
private int[][] leftDcContext = []; |
|
|
private object[] referenceFrame = []; |
|
|
|
|
|
private object[][][] referenceFrames = []; |
|
|
|
|
|
private int paletteSizeY; |
|
|
|
|
|
private int paletteSizeUv; |
|
|
|
|
|
private object[][] aboveLevelContext = []; |
|
|
|
|
|
private object[][] aboveDcContext = []; |
|
|
|
|
|
private object[][] leftLevelContext = []; |
|
|
|
|
|
private object[][] leftDcContext = []; |
|
|
|
|
|
private Av1TransformSize transformSize = Av1TransformSize.Size4x4; |
|
|
|
|
|
private object filterUltraMode = -1; |
|
|
|
|
|
private int angleDeltaY; |
|
|
|
|
|
private int angleDeltaUv; |
|
|
|
|
|
private bool lossless; |
|
|
|
|
|
private int[][] segmentIds = []; |
|
|
private int[][] segmentIds = []; |
|
|
private int maxLumaWidth; |
|
|
private int maxLumaWidth; |
|
|
private int maxLumaHeight; |
|
|
private int maxLumaHeight; |
|
|
private int segmentId; |
|
|
|
|
|
private int[][] cdefIndex = []; |
|
|
|
|
|
private int deltaLoopFilterResolution = -1; |
|
|
private int deltaLoopFilterResolution = -1; |
|
|
private int deltaQuantizerResolution = -1; |
|
|
private int deltaQuantizerResolution = -1; |
|
|
|
|
|
|
|
|
@ -74,9 +53,10 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
public void DecodeTile(Span<byte> tileData, int tileNum) |
|
|
public void DecodeTile(Span<byte> tileData, int tileNum) |
|
|
{ |
|
|
{ |
|
|
Av1SymbolDecoder reader = new(tileData); |
|
|
Av1SymbolDecoder reader = new(tileData); |
|
|
int tileRowIndex = tileNum / this.TileInfo.TileColumnCount; |
|
|
|
|
|
int tileColumnIndex = tileNum % this.TileInfo.TileColumnCount; |
|
|
int tileColumnIndex = tileNum % this.TileInfo.TileColumnCount; |
|
|
this.aboveContext.Clear(); |
|
|
int tileRowIndex = tileNum / this.TileInfo.TileColumnCount; |
|
|
|
|
|
|
|
|
|
|
|
this.aboveContext.Clear(this.TileInfo.TileColumnStartModeInfo[tileColumnIndex], this.TileInfo.TileColumnStartModeInfo[tileColumnIndex - 1]); |
|
|
this.ClearLoopFilterDelta(); |
|
|
this.ClearLoopFilterDelta(); |
|
|
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount; |
|
|
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount; |
|
|
this.referenceSgrXqd = new int[planesCount][]; |
|
|
this.referenceSgrXqd = new int[planesCount][]; |
|
|
@ -111,12 +91,13 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
// this.ClearCdef(row, column);
|
|
|
// this.ClearCdef(row, column);
|
|
|
this.ClearBlockDecodedFlags(row, column, superBlock4x4Size); |
|
|
this.ClearBlockDecodedFlags(row, column, superBlock4x4Size); |
|
|
this.ReadLoopRestoration(row, column, superBlockSize); |
|
|
this.ReadLoopRestoration(row, column, superBlockSize); |
|
|
this.DecodePartition(ref reader, row, column, superBlockSize); |
|
|
this.ParsePartition(ref reader, row, column, superBlockSize); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void ClearLoopFilterDelta() => this.deltaLoopFilter = new int[4]; |
|
|
private void ClearLoopFilterDelta() |
|
|
|
|
|
=> this.deltaLoopFilter = new int[4]; |
|
|
|
|
|
|
|
|
private void ClearBlockDecodedFlags(int row, int column, int superBlock4x4Size) |
|
|
private void ClearBlockDecodedFlags(int row, int column, int superBlock4x4Size) |
|
|
{ |
|
|
{ |
|
|
@ -169,16 +150,17 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
// TODO: Implement
|
|
|
// TODO: Implement
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void DecodePartition(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
private void ParsePartition(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
{ |
|
|
{ |
|
|
|
|
|
Av1SuperblockInfo superblockInfo = new(); |
|
|
if (rowIndex >= this.TileInfo.TileRowStartModeInfo[rowIndex] || |
|
|
if (rowIndex >= this.TileInfo.TileRowStartModeInfo[rowIndex] || |
|
|
columnIndex >= this.TileInfo.TileColumnStartModeInfo[columnIndex]) |
|
|
columnIndex >= this.TileInfo.TileColumnStartModeInfo[columnIndex]) |
|
|
{ |
|
|
{ |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.availableUp = this.IsInside(rowIndex - 1, columnIndex); |
|
|
bool availableUp = this.IsInside(rowIndex - 1, columnIndex); |
|
|
this.availableLeft = this.IsInside(rowIndex, columnIndex - 1); |
|
|
bool availableLeft = this.IsInside(rowIndex, columnIndex - 1); |
|
|
int block4x4Size = blockSize.Get4x4WideCount(); |
|
|
int block4x4Size = blockSize.Get4x4WideCount(); |
|
|
int halfBlock4x4Size = block4x4Size >> 1; |
|
|
int halfBlock4x4Size = block4x4Size >> 1; |
|
|
int quarterBlock4x4Size = halfBlock4x4Size >> 2; |
|
|
int quarterBlock4x4Size = halfBlock4x4Size >> 2; |
|
|
@ -212,82 +194,86 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
switch (partitionType) |
|
|
switch (partitionType) |
|
|
{ |
|
|
{ |
|
|
case Av1PartitionType.Split: |
|
|
case Av1PartitionType.Split: |
|
|
this.DecodePartition(ref reader, rowIndex, columnIndex, subSize); |
|
|
this.ParsePartition(ref reader, rowIndex, columnIndex, subSize); |
|
|
this.DecodePartition(ref reader, rowIndex, columnIndex + halfBlock4x4Size, subSize); |
|
|
this.ParsePartition(ref reader, rowIndex, columnIndex + halfBlock4x4Size, subSize); |
|
|
this.DecodePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex, subSize); |
|
|
this.ParsePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex, subSize); |
|
|
this.DecodePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex + halfBlock4x4Size, subSize); |
|
|
this.ParsePartition(ref reader, rowIndex + halfBlock4x4Size, columnIndex + halfBlock4x4Size, subSize); |
|
|
break; |
|
|
break; |
|
|
case Av1PartitionType.None: |
|
|
case Av1PartitionType.None: |
|
|
this.DecodeBlock(ref reader, rowIndex, columnIndex, subSize); |
|
|
this.ParseBlock(ref reader, rowIndex, columnIndex, subSize, superblockInfo, Av1PartitionType.None); |
|
|
break; |
|
|
break; |
|
|
default: |
|
|
default: |
|
|
throw new NotImplementedException($"Partition type: {partitionType} is not supported."); |
|
|
throw new NotImplementedException($"Partition type: {partitionType} is not supported."); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void DecodeBlock(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
private void ParseBlock(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize, Av1SuperblockInfo superblockInfo, Av1PartitionType partitionType) |
|
|
{ |
|
|
{ |
|
|
int block4x4Width = blockSize.Get4x4WideCount(); |
|
|
int block4x4Width = blockSize.Get4x4WideCount(); |
|
|
int block4x4Height = blockSize.Get4x4HighCount(); |
|
|
int block4x4Height = blockSize.Get4x4HighCount(); |
|
|
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount; |
|
|
int planesCount = this.SequenceHeader.ColorConfig.ChannelCount; |
|
|
bool hasChroma = planesCount > 1; |
|
|
Av1BlockModeInfo blockModeInfo = new(planesCount, blockSize); |
|
|
if (block4x4Height == 1 && this.SequenceHeader.ColorConfig.SubSamplingY && (rowIndex & 0x1) == 0) |
|
|
bool hasChroma = this.HasChroma(rowIndex, columnIndex, blockSize); |
|
|
{ |
|
|
Av1PartitionInfo partitionInfo = new(blockModeInfo, superblockInfo, hasChroma, partitionType); |
|
|
hasChroma = false; |
|
|
partitionInfo.ColumnIndex = columnIndex; |
|
|
} |
|
|
partitionInfo.RowIndex = rowIndex; |
|
|
|
|
|
partitionInfo.AvailableUp = this.IsInside(rowIndex - 1, columnIndex); |
|
|
if (block4x4Width == 1 && this.SequenceHeader.ColorConfig.SubSamplingX && (columnIndex & 0x1) == 0) |
|
|
partitionInfo.AvailableLeft = this.IsInside(rowIndex, columnIndex - 1); |
|
|
{ |
|
|
partitionInfo.AvailableUpForChroma = partitionInfo.AvailableUp; |
|
|
hasChroma = false; |
|
|
partitionInfo.AvailableLeftForChroma = partitionInfo.AvailableLeft; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
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 (hasChroma) |
|
|
{ |
|
|
{ |
|
|
if (this.SequenceHeader.ColorConfig.SubSamplingY && block4x4Height == 1) |
|
|
if (this.SequenceHeader.ColorConfig.SubSamplingY && block4x4Height == 1) |
|
|
{ |
|
|
{ |
|
|
this.availableUpForChroma = this.IsInside(rowIndex - 2, columnIndex); |
|
|
partitionInfo.AvailableUpForChroma = this.IsInside(rowIndex - 2, columnIndex); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.SequenceHeader.ColorConfig.SubSamplingX && block4x4Width == 1) |
|
|
if (this.SequenceHeader.ColorConfig.SubSamplingX && block4x4Width == 1) |
|
|
{ |
|
|
{ |
|
|
this.availableLeftForChroma = this.IsInside(rowIndex, columnIndex - 2); |
|
|
partitionInfo.AvailableLeftForChroma = this.IsInside(rowIndex, columnIndex - 2); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.ReadModeInfo(ref reader, rowIndex, columnIndex, blockSize); |
|
|
if (partitionInfo.AvailableUp) |
|
|
this.ReadPaletteTokens(ref reader); |
|
|
|
|
|
ReadBlockTransformSize(ref reader, rowIndex, columnIndex, blockSize); |
|
|
|
|
|
if (this.skip) |
|
|
|
|
|
{ |
|
|
{ |
|
|
this.ResetBlockContext(rowIndex, columnIndex, blockSize); |
|
|
partitionInfo.AboveModeInfo = superblockInfo.GetModeInfo(rowIndex - 1, columnIndex); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// bool isCompound = false;
|
|
|
if (partitionInfo.AvailableLeft) |
|
|
for (int y = 0; y < block4x4Height; y++) |
|
|
|
|
|
{ |
|
|
{ |
|
|
for (int x = 0; x < block4x4Width; x++) |
|
|
partitionInfo.LeftModeInfo = superblockInfo.GetModeInfo(rowIndex, columnIndex - 1); |
|
|
{ |
|
|
} |
|
|
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.ReadModeInfo(ref reader, partitionInfo); |
|
|
{ |
|
|
ReadPaletteTokens(ref reader, partitionInfo); |
|
|
this.referenceFrames[rowIndex + y][columnIndex + x][refList] = this.referenceFrame[refList]; |
|
|
ReadBlockTransformSize(ref reader, rowIndex, columnIndex, blockSize); |
|
|
} |
|
|
if (partitionInfo.ModeInfo.Skip) |
|
|
} |
|
|
{ |
|
|
|
|
|
this.ResetSkipContext(partitionInfo); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
ComputePrediction(); |
|
|
|
|
|
this.Residual(rowIndex, columnIndex, blockSize); |
|
|
this.Residual(rowIndex, columnIndex, blockSize); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void ResetSkipContext(Av1PartitionInfo partitionInfo) |
|
|
|
|
|
{ |
|
|
|
|
|
int planesCount = this.SequenceHeader.ColorConfig.IsMonochrome ? 1 : 3; |
|
|
|
|
|
for (int i = 0; i < planesCount; i++) |
|
|
|
|
|
{ |
|
|
|
|
|
bool subX = i > 0 && this.SequenceHeader.ColorConfig.SubSamplingX; |
|
|
|
|
|
bool subY = i > 0 && this.SequenceHeader.ColorConfig.SubSamplingY; |
|
|
|
|
|
Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY); |
|
|
|
|
|
int txsWide = planeBlockSize.GetWidth() >> 2; |
|
|
|
|
|
int txsHigh = planeBlockSize.GetHeight() >> 2; |
|
|
|
|
|
int aboveOffset = (partitionInfo.ColumnIndex - this.TileInfo.TileColumnStartModeInfo[partitionInfo.ColumnIndex]) >> (subX ? 1 : 0); |
|
|
|
|
|
int leftOffset = (partitionInfo.RowIndex - this.TileInfo.TileRowStartModeInfo[partitionInfo.RowIndex]) >> (subY ? 1 : 0); |
|
|
|
|
|
int[] aboveContext = this.aboveContext.AboveContext[i + aboveOffset]; |
|
|
|
|
|
int[] leftContext = this.leftContext.LeftContext[i + leftOffset]; |
|
|
|
|
|
Array.Fill(aboveContext, 0); |
|
|
|
|
|
Array.Fill(leftContext, 0); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private void Residual(int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
private void Residual(int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
{ |
|
|
{ |
|
|
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; |
|
|
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; |
|
|
@ -307,7 +293,7 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
int subBlockColumn = columnChunk & superBlockMask; |
|
|
int subBlockColumn = columnChunk & superBlockMask; |
|
|
for (int plane = 0; plane < 1 + (this.HasChroma(rowIndex, columnIndex, blockSize) ? 2 : 0); plane++) |
|
|
for (int plane = 0; plane < 1 + (this.HasChroma(rowIndex, columnIndex, blockSize) ? 2 : 0); plane++) |
|
|
{ |
|
|
{ |
|
|
Av1TransformSize transformSize = this.FrameInfo.CodedLossless ? Av1TransformSize.Size4x4 : this.GetSize(plane, this.transformSize); |
|
|
Av1TransformSize transformSize = this.FrameInfo.CodedLossless ? Av1TransformSize.Size4x4 : this.GetSize(plane, -1); |
|
|
int stepX = transformSize.GetWidth() >> 2; |
|
|
int stepX = transformSize.GetWidth() >> 2; |
|
|
int stepY = transformSize.GetHeight() >> 2; |
|
|
int stepY = transformSize.GetHeight() >> 2; |
|
|
Av1BlockSize planeSize = this.GetPlaneResidualSize(sizeChunk, plane); |
|
|
Av1BlockSize planeSize = this.GetPlaneResidualSize(sizeChunk, plane); |
|
|
@ -348,6 +334,7 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
|
|
|
|
|
|
private void TransformBlock(int plane, int baseX, int baseY, Av1TransformSize transformSize, int x, int y) |
|
|
private void TransformBlock(int plane, int baseX, int baseY, Av1TransformSize transformSize, int x, int y) |
|
|
{ |
|
|
{ |
|
|
|
|
|
Av1PartitionInfo partitionInfo = new(new(1, Av1BlockSize.Invalid), new(), false, Av1PartitionType.None); |
|
|
int startX = (baseX + 4) * x; |
|
|
int startX = (baseX + 4) * x; |
|
|
int startY = (baseY + 4) * y; |
|
|
int startY = (baseY + 4) * y; |
|
|
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; |
|
|
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; |
|
|
@ -368,28 +355,28 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if ((plane == 0 && this.paletteSizeY > 0) || |
|
|
if ((plane == 0 && partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) > 0) || |
|
|
(plane != 0 && this.paletteSizeUv > 0)) |
|
|
(plane != 0 && partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Uv) > 0)) |
|
|
{ |
|
|
{ |
|
|
this.PredictPalette(plane, startX, startY, x, y, transformSize); |
|
|
this.PredictPalette(plane, startX, startY, x, y, transformSize); |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
bool isChromaFromLuma = plane > 0 && this.uvMode == Av1PredictionMode.UvChromaFromLuma; |
|
|
bool isChromaFromLuma = plane > 0 && partitionInfo.ModeInfo.UvMode == Av1PredictionMode.UvChromaFromLuma; |
|
|
Av1PredictionMode mode; |
|
|
Av1PredictionMode mode; |
|
|
if (plane == 0) |
|
|
if (plane == 0) |
|
|
{ |
|
|
{ |
|
|
mode = this.yMode; |
|
|
mode = partitionInfo.ModeInfo.YMode; |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
mode = isChromaFromLuma ? Av1PredictionMode.DC : this.uvMode; |
|
|
mode = isChromaFromLuma ? Av1PredictionMode.DC : partitionInfo.ModeInfo.UvMode; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
int log2Width = transformSize.GetWidthLog2(); |
|
|
int log2Width = transformSize.GetWidthLog2(); |
|
|
int log2Height = transformSize.GetHeightLog2(); |
|
|
int log2Height = transformSize.GetHeightLog2(); |
|
|
bool leftAvailable = x > 0 || plane == 0 ? this.availableLeft : this.availableLeftForChroma; |
|
|
bool leftAvailable = x > 0 || plane == 0 ? partitionInfo.AvailableLeft : partitionInfo.AvailableLeftForChroma; |
|
|
bool upAvailable = y > 0 || plane == 0 ? this.availableUp : this.availableUpForChroma; |
|
|
bool upAvailable = y > 0 || plane == 0 ? partitionInfo.AvailableUp : partitionInfo.AvailableUpForChroma; |
|
|
bool haveAboveRight = this.blockDecoded[plane][(subBlockRow >> subY) - 1][(subBlockColumn >> subX) + stepX]; |
|
|
bool haveAboveRight = this.blockDecoded[plane][(subBlockRow >> subY) - 1][(subBlockColumn >> subX) + stepX]; |
|
|
bool haveBelowLeft = this.blockDecoded[plane][(subBlockRow >> subY) + stepY][(subBlockColumn >> subX) - 1]; |
|
|
bool haveBelowLeft = this.blockDecoded[plane][(subBlockRow >> subY) + stepY][(subBlockColumn >> subX) - 1]; |
|
|
this.PredictIntra(plane, startX, startY, leftAvailable, upAvailable, haveAboveRight, haveBelowLeft, mode, log2Width, log2Height); |
|
|
this.PredictIntra(plane, startX, startY, leftAvailable, upAvailable, haveAboveRight, haveBelowLeft, mode, log2Width, log2Height); |
|
|
@ -405,7 +392,7 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
this.maxLumaHeight = startY + (stepY * 4); |
|
|
this.maxLumaHeight = startY + (stepY * 4); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (!this.skip) |
|
|
if (!partitionInfo.ModeInfo.Skip) |
|
|
{ |
|
|
{ |
|
|
int eob = this.Coefficients(plane, startX, startY, transformSize); |
|
|
int eob = this.Coefficients(plane, startX, startY, transformSize); |
|
|
if (eob > 0) |
|
|
if (eob > 0) |
|
|
@ -434,11 +421,6 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
|
|
|
|
|
|
private void PredictPalette(int plane, int startX, int startY, int x, int y, Av1TransformSize transformSize) => throw new NotImplementedException(); |
|
|
private void PredictPalette(int plane, int startX, int startY, int x, int y, Av1TransformSize transformSize) => throw new NotImplementedException(); |
|
|
|
|
|
|
|
|
private static void ComputePrediction() |
|
|
|
|
|
{ |
|
|
|
|
|
// Not applicable for INTRA frames.
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void ResetBlockContext(int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
private void ResetBlockContext(int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
{ |
|
|
{ |
|
|
int block4x4Width = blockSize.Get4x4WideCount(); |
|
|
int block4x4Width = blockSize.Get4x4WideCount(); |
|
|
@ -446,21 +428,22 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; |
|
|
bool subsamplingX = this.SequenceHeader.ColorConfig.SubSamplingX; |
|
|
bool subsamplingY = this.SequenceHeader.ColorConfig.SubSamplingY; |
|
|
bool subsamplingY = this.SequenceHeader.ColorConfig.SubSamplingY; |
|
|
int endPlane = this.HasChroma(rowIndex, columnIndex, blockSize) ? 3 : 1; |
|
|
int endPlane = this.HasChroma(rowIndex, columnIndex, blockSize) ? 3 : 1; |
|
|
|
|
|
this.aboveLevelContext = new int[3][]; |
|
|
|
|
|
this.aboveDcContext = new int[3][]; |
|
|
|
|
|
this.leftLevelContext = new int[3][]; |
|
|
|
|
|
this.leftDcContext = new int[3][]; |
|
|
for (int plane = 0; plane < endPlane; plane++) |
|
|
for (int plane = 0; plane < endPlane; plane++) |
|
|
{ |
|
|
{ |
|
|
int subX = plane > 0 && subsamplingX ? 1 : 0; |
|
|
int subX = plane > 0 && subsamplingX ? 1 : 0; |
|
|
int subY = plane > 0 && subsamplingY ? 1 : 0; |
|
|
int subY = plane > 0 && subsamplingY ? 1 : 0; |
|
|
for (int i = columnIndex >> subX; i < (columnIndex + block4x4Width) >> subX; i++) |
|
|
this.aboveLevelContext[plane] = new int[(columnIndex + block4x4Width) >> subX]; |
|
|
{ |
|
|
this.aboveDcContext[plane] = new int[(columnIndex + block4x4Width) >> subX]; |
|
|
this.aboveLevelContext[plane][i] = 0; |
|
|
this.leftLevelContext[plane] = new int[(rowIndex + block4x4Height) >> subY]; |
|
|
this.aboveDcContext[plane][i] = 0; |
|
|
this.leftDcContext[plane] = new int[(rowIndex + block4x4Height) >> subY]; |
|
|
} |
|
|
Array.Fill(this.aboveLevelContext[plane], 0); |
|
|
|
|
|
Array.Fill(this.aboveDcContext[plane], 0); |
|
|
for (int i = rowIndex >> subY; i < (rowIndex + block4x4Height) >> subY; i++) |
|
|
Array.Fill(this.leftLevelContext[plane], 0); |
|
|
{ |
|
|
Array.Fill(this.leftDcContext[plane], 0); |
|
|
this.leftLevelContext[plane][i] = 0; |
|
|
|
|
|
this.leftDcContext[plane][i] = 0; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -480,209 +463,250 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
}*/ |
|
|
}*/ |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void ReadPaletteTokens(ref Av1SymbolDecoder reader) |
|
|
private static void ReadPaletteTokens(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
{ |
|
|
{ |
|
|
reader.ReadLiteral(-1); |
|
|
reader.ReadLiteral(-1); |
|
|
if (this.paletteSizeY != 0) |
|
|
if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) != 0) |
|
|
{ |
|
|
{ |
|
|
// Todo: Implement.
|
|
|
// Todo: Implement.
|
|
|
throw new NotImplementedException(); |
|
|
throw new NotImplementedException(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.paletteSizeUv != 0) |
|
|
if (partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Uv) != 0) |
|
|
{ |
|
|
{ |
|
|
// Todo: Implement.
|
|
|
// Todo: Implement.
|
|
|
throw new NotImplementedException(); |
|
|
throw new NotImplementedException(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void ReadModeInfo(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
private void ReadModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
=> this.ReadIntraFrameModeInfo(ref reader, rowIndex, columnIndex, blockSize); |
|
|
{ |
|
|
|
|
|
DebugGuard.IsTrue(this.FrameInfo.FrameType is ObuFrameType.KeyFrame or ObuFrameType.IntraOnlyFrame, "Only INTRA frames supported."); |
|
|
|
|
|
this.ReadIntraFrameModeInfo(ref reader, partitionInfo); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
private void ReadIntraFrameModeInfo(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
private void ReadIntraFrameModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
{ |
|
|
{ |
|
|
this.skip = false; |
|
|
|
|
|
if (this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip) |
|
|
if (this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip) |
|
|
{ |
|
|
{ |
|
|
this.ReadIntraSegmentId(ref reader); |
|
|
this.IntraSegmentId(ref reader, partitionInfo); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// this.skipMode = false;
|
|
|
// this.skipMode = false;
|
|
|
this.ReadSkip(ref reader); |
|
|
partitionInfo.ModeInfo.Skip = this.ReadSkip(ref reader, partitionInfo); |
|
|
if (!this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip) |
|
|
if (!this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip) |
|
|
{ |
|
|
{ |
|
|
this.IntraSegmentId(ref reader, rowIndex, columnIndex); |
|
|
this.IntraSegmentId(ref reader, partitionInfo); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.ReadCdef(ref reader, rowIndex, columnIndex, blockSize); |
|
|
this.ReadCdef(ref reader, partitionInfo); |
|
|
this.ReadDeltaQuantizerIndex(ref reader, blockSize); |
|
|
|
|
|
this.ReadDeltaLoopFilter(ref reader, blockSize); |
|
|
bool readDeltas = false; |
|
|
this.readDeltas = false; |
|
|
if (readDeltas) |
|
|
this.referenceFrame[0] = -1; // IntraFrame;
|
|
|
{ |
|
|
this.referenceFrame[1] = -1; // None;
|
|
|
this.ReadDeltaQuantizerIndex(ref reader, partitionInfo); |
|
|
|
|
|
this.ReadDeltaLoopFilter(ref reader, partitionInfo); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
partitionInfo.ReferenceFrame[0] = 0; // IntraFrame;
|
|
|
|
|
|
partitionInfo.ReferenceFrame[1] = -1; // None;
|
|
|
|
|
|
partitionInfo.ModeInfo.SetPaletteSizes(0, 0); |
|
|
bool useIntraBlockCopy = false; |
|
|
bool useIntraBlockCopy = false; |
|
|
if (this.FrameInfo.AllowIntraBlockCopy) |
|
|
if (this.AllowIntraBlockCopy()) |
|
|
{ |
|
|
{ |
|
|
useIntraBlockCopy = reader.ReadUseIntraBlockCopy(); |
|
|
useIntraBlockCopy = reader.ReadUseIntraBlockCopy(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (useIntraBlockCopy) |
|
|
if (useIntraBlockCopy) |
|
|
{ |
|
|
{ |
|
|
// TODO: Implement
|
|
|
partitionInfo.ModeInfo.YMode = Av1PredictionMode.DC; |
|
|
|
|
|
partitionInfo.ModeInfo.UvMode = Av1PredictionMode.DC; |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
// this.IsInter = false;
|
|
|
// this.IsInter = false;
|
|
|
this.yMode = reader.ReadIntraFrameYMode(blockSize); |
|
|
partitionInfo.ModeInfo.YMode = reader.ReadYMode(partitionInfo.AboveModeInfo, partitionInfo.LeftModeInfo); |
|
|
this.IntraAngleInfoY(ref reader, blockSize); |
|
|
partitionInfo.ModeInfo.AngleDelta[(int)Av1PlaneType.Y] = IntraAngleInfo(ref reader, partitionInfo.ModeInfo.YMode, partitionInfo.ModeInfo.BlockSize); |
|
|
if (this.HasChroma(rowIndex, columnIndex, blockSize)) |
|
|
if (partitionInfo.IsChroma && !this.SequenceHeader.ColorConfig.IsMonochrome) |
|
|
{ |
|
|
{ |
|
|
this.uvMode = reader.ReadUvMode(blockSize, this.IsChromaForLumaAllowed(blockSize)); |
|
|
partitionInfo.ModeInfo.UvMode = reader.ReadIntraModeUv(partitionInfo.ModeInfo.YMode, this.IsChromaForLumaAllowed(partitionInfo)); |
|
|
if (this.uvMode == Av1PredictionMode.UvChromaFromLuma) |
|
|
if (partitionInfo.ModeInfo.UvMode == Av1PredictionMode.UvChromaFromLuma) |
|
|
{ |
|
|
{ |
|
|
this.ReadChromaFromLumaAlphas(ref reader); |
|
|
this.ReadChromaFromLumaAlphas(ref reader, partitionInfo); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.IntraAngleInfoUv(ref reader, blockSize); |
|
|
partitionInfo.ModeInfo.AngleDelta[(int)Av1PlaneType.Uv] = IntraAngleInfo(ref reader, partitionInfo.ModeInfo.UvMode, partitionInfo.ModeInfo.BlockSize); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
partitionInfo.ModeInfo.UvMode = Av1PredictionMode.DC; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.paletteSizeY = 0; |
|
|
if (partitionInfo.ModeInfo.BlockSize >= Av1BlockSize.Block8x8 && |
|
|
this.paletteSizeUv = 0; |
|
|
partitionInfo.ModeInfo.BlockSize.GetWidth() <= 64 && |
|
|
if (this.SequenceHeader.ModeInfoSize >= (int)Av1BlockSize.Block8x8 && |
|
|
partitionInfo.ModeInfo.BlockSize.GetHeight() <= 64 && |
|
|
((Av1BlockSize)this.SequenceHeader.ModeInfoSize).Get4x4WideCount() <= 64 && |
|
|
|
|
|
((Av1BlockSize)this.SequenceHeader.ModeInfoSize).Get4x4HighCount() <= 64 && |
|
|
|
|
|
this.FrameInfo.AllowScreenContentTools) |
|
|
this.FrameInfo.AllowScreenContentTools) |
|
|
{ |
|
|
{ |
|
|
this.PaletteModeInfo(ref reader); |
|
|
this.PaletteModeInfo(ref reader, partitionInfo); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.FilterIntraModeInfo(ref reader, blockSize); |
|
|
this.FilterIntraModeInfo(ref reader, partitionInfo); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private bool IsChromaForLumaAllowed(Av1BlockSize blockSize) |
|
|
private bool AllowIntraBlockCopy() |
|
|
|
|
|
=> (this.FrameInfo.FrameType is ObuFrameType.KeyFrame or ObuFrameType.IntraOnlyFrame) && |
|
|
|
|
|
(this.SequenceHeader.ForceScreenContentTools > 0) && |
|
|
|
|
|
this.FrameInfo.AllowIntraBlockCopy; |
|
|
|
|
|
|
|
|
|
|
|
private bool IsChromaForLumaAllowed(Av1PartitionInfo partitionInfo) |
|
|
{ |
|
|
{ |
|
|
if (this.lossless) |
|
|
if (this.FrameInfo.LosslessArray[partitionInfo.ModeInfo.SegmentId]) |
|
|
{ |
|
|
{ |
|
|
// In lossless, CfL is available when the partition size is equal to the
|
|
|
// In lossless, CfL is available when the partition size is equal to the
|
|
|
// transform size.
|
|
|
// transform size.
|
|
|
bool subX = this.SequenceHeader.ColorConfig.SubSamplingX; |
|
|
bool subX = this.SequenceHeader.ColorConfig.SubSamplingX; |
|
|
bool subY = this.SequenceHeader.ColorConfig.SubSamplingY; |
|
|
bool subY = this.SequenceHeader.ColorConfig.SubSamplingY; |
|
|
Av1BlockSize planeBlockSize = blockSize.GetSubsampled(subX, subY); |
|
|
Av1BlockSize planeBlockSize = partitionInfo.ModeInfo.BlockSize.GetSubsampled(subX, subY); |
|
|
return planeBlockSize == Av1BlockSize.Block4x4; |
|
|
return planeBlockSize == Av1BlockSize.Block4x4; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Spec: CfL is available to luma partitions lesser than or equal to 32x32
|
|
|
// Spec: CfL is available to luma partitions lesser than or equal to 32x32
|
|
|
return blockSize.Get4x4WideCount() <= 32 && blockSize.Get4x4HighCount() <= 32; |
|
|
return partitionInfo.ModeInfo.BlockSize.GetWidth() <= 32 && partitionInfo.ModeInfo.BlockSize.GetHeight() <= 32; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void ReadIntraSegmentId(ref Av1SymbolDecoder reader) => throw new NotImplementedException(); |
|
|
private void FilterIntraModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
|
|
|
|
|
|
private void FilterIntraModeInfo(ref Av1SymbolDecoder reader, Av1BlockSize blockSize) |
|
|
|
|
|
{ |
|
|
{ |
|
|
bool useFilterIntra = false; |
|
|
|
|
|
if (this.SequenceHeader.EnableFilterIntra && |
|
|
if (this.SequenceHeader.EnableFilterIntra && |
|
|
this.yMode == Av1PredictionMode.DC && this.paletteSizeY == 0 && |
|
|
partitionInfo.ModeInfo.YMode == Av1PredictionMode.DC && |
|
|
Math.Max(blockSize.GetWidth(), blockSize.GetHeight()) <= 32) |
|
|
partitionInfo.ModeInfo.GetPaletteSize(Av1PlaneType.Y) == 0 && |
|
|
|
|
|
Math.Max(partitionInfo.ModeInfo.BlockSize.GetWidth(), partitionInfo.ModeInfo.BlockSize.GetHeight()) <= 32) |
|
|
{ |
|
|
{ |
|
|
useFilterIntra = reader.ReadUseFilterUltra(); |
|
|
partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = reader.ReadUseFilterUltra(partitionInfo.ModeInfo.BlockSize); |
|
|
if (useFilterIntra) |
|
|
if (partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra) |
|
|
{ |
|
|
{ |
|
|
this.filterUltraMode = reader.ReadFilterUltraMode(); |
|
|
partitionInfo.ModeInfo.FilterIntraModeInfo.Mode = reader.ReadFilterUltraMode(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
partitionInfo.ModeInfo.FilterIntraModeInfo.UseFilterIntra = false; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void PaletteModeInfo(ref Av1SymbolDecoder reader) => |
|
|
private void PaletteModeInfo(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) => |
|
|
|
|
|
|
|
|
// TODO: Implement.
|
|
|
// TODO: Implement.
|
|
|
throw new NotImplementedException(); |
|
|
throw new NotImplementedException(); |
|
|
|
|
|
|
|
|
private void ReadChromaFromLumaAlphas(ref Av1SymbolDecoder reader) => |
|
|
private void ReadChromaFromLumaAlphas(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) => |
|
|
|
|
|
|
|
|
// TODO: Implement.
|
|
|
// TODO: Implement.
|
|
|
throw new NotImplementedException(); |
|
|
throw new NotImplementedException(); |
|
|
|
|
|
|
|
|
private void IntraAngleInfoY(ref Av1SymbolDecoder reader, Av1BlockSize blockSize) |
|
|
private static int IntraAngleInfo(ref Av1SymbolDecoder reader, Av1PredictionMode mode, Av1BlockSize blockSize) |
|
|
{ |
|
|
{ |
|
|
this.angleDeltaY = 0; |
|
|
int angleDelta = 0; |
|
|
if (blockSize >= Av1BlockSize.Block8x8 && IsDirectionalMode(this.yMode)) |
|
|
if (blockSize >= Av1BlockSize.Block8x8 && IsDirectionalMode(mode)) |
|
|
{ |
|
|
{ |
|
|
int angleDeltaY = reader.ReadAngleDelta(this.yMode); |
|
|
int symbol = reader.ReadAngleDelta(mode); |
|
|
this.angleDeltaY = angleDeltaY - ObuConstants.MaxAngleDelta; |
|
|
angleDelta = symbol - ObuConstants.MaxAngleDelta; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void IntraAngleInfoUv(ref Av1SymbolDecoder reader, Av1BlockSize blockSize) |
|
|
return angleDelta; |
|
|
{ |
|
|
|
|
|
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) |
|
|
private static bool IsDirectionalMode(Av1PredictionMode mode) |
|
|
=> mode is >= Av1PredictionMode.Vertical and <= Av1PredictionMode.Directional67Degrees; |
|
|
=> mode is >= Av1PredictionMode.Vertical and <= Av1PredictionMode.Directional67Degrees; |
|
|
|
|
|
|
|
|
private void IntraSegmentId(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex) |
|
|
private void IntraSegmentId(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
{ |
|
|
{ |
|
|
if (this.FrameInfo.SegmentationParameters.Enabled) |
|
|
if (this.FrameInfo.SegmentationParameters.Enabled) |
|
|
{ |
|
|
{ |
|
|
this.ReadSegmentId(ref reader, rowIndex, columnIndex); |
|
|
this.ReadSegmentId(ref reader, partitionInfo); |
|
|
} |
|
|
} |
|
|
else |
|
|
|
|
|
|
|
|
int bw4 = partitionInfo.ModeInfo.BlockSize.Get4x4WideCount(); |
|
|
|
|
|
int bh4 = partitionInfo.ModeInfo.BlockSize.Get4x4HighCount(); |
|
|
|
|
|
int x_mis = Math.Min(this.FrameInfo.ModeInfoColumnCount - partitionInfo.ColumnIndex, bw4); |
|
|
|
|
|
int y_mis = Math.Min(this.FrameInfo.ModeInfoRowCount - partitionInfo.RowIndex, bh4); |
|
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < y_mis; y++) |
|
|
{ |
|
|
{ |
|
|
this.segmentId = 0; |
|
|
for (int x = 0; x < x_mis; x++) |
|
|
|
|
|
{ |
|
|
|
|
|
this.segmentIds[partitionInfo.RowIndex + y][partitionInfo.ColumnIndex + x] = partitionInfo.ModeInfo.SegmentId; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
this.lossless = this.FrameInfo.LosslessArray[this.segmentId]; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void ReadSegmentId(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex) |
|
|
private void ReadSegmentId(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
{ |
|
|
{ |
|
|
int pred; |
|
|
int predictor; |
|
|
int prevUL = -1; |
|
|
int prevUL = -1; |
|
|
int prevU = -1; |
|
|
int prevU = -1; |
|
|
int prevL = -1; |
|
|
int prevL = -1; |
|
|
if (this.availableUp && this.availableLeft) |
|
|
int columnIndex = partitionInfo.ColumnIndex; |
|
|
|
|
|
int rowIndex = partitionInfo.RowIndex; |
|
|
|
|
|
if (partitionInfo.AvailableUp && partitionInfo.AvailableLeft) |
|
|
{ |
|
|
{ |
|
|
prevUL = this.segmentIds[rowIndex - 1][columnIndex - 1]; |
|
|
prevUL = this.GetSegmentId(partitionInfo, rowIndex - 1, columnIndex - 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.availableUp) |
|
|
if (partitionInfo.AvailableUp) |
|
|
{ |
|
|
{ |
|
|
prevU = this.segmentIds[rowIndex - 1][columnIndex]; |
|
|
prevU = this.GetSegmentId(partitionInfo, rowIndex - 1, columnIndex); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.availableLeft) |
|
|
if (partitionInfo.AvailableLeft) |
|
|
{ |
|
|
{ |
|
|
prevU = this.segmentIds[rowIndex][columnIndex - 1]; |
|
|
prevU = this.GetSegmentId(partitionInfo, rowIndex, columnIndex - 1); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (prevU == -1) |
|
|
if (prevU == -1) |
|
|
{ |
|
|
{ |
|
|
pred = prevL == -1 ? 0 : prevL; |
|
|
predictor = prevL == -1 ? 0 : prevL; |
|
|
} |
|
|
} |
|
|
else if (prevL == -1) |
|
|
else if (prevL == -1) |
|
|
{ |
|
|
{ |
|
|
pred = prevU; |
|
|
predictor = prevU; |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
pred = prevU == prevUL ? prevU : prevL; |
|
|
predictor = prevU == prevUL ? prevU : prevL; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.skip) |
|
|
if (partitionInfo.ModeInfo.Skip) |
|
|
{ |
|
|
{ |
|
|
this.segmentId = 0; |
|
|
partitionInfo.ModeInfo.SegmentId = predictor; |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
|
|
|
int ctx = prevUL < 0 ? 0 /* Edge cases */ |
|
|
|
|
|
: prevUL == prevU && prevUL == prevL ? 2 |
|
|
|
|
|
: prevUL == prevU || prevUL == prevL || prevU == prevL ? 1 : 0; |
|
|
int lastActiveSegmentId = this.FrameInfo.SegmentationParameters.LastActiveSegmentId; |
|
|
int lastActiveSegmentId = this.FrameInfo.SegmentationParameters.LastActiveSegmentId; |
|
|
this.segmentId = NegativeDeinterleave(reader.ReadSegmentId(-1), pred, lastActiveSegmentId + 1); |
|
|
partitionInfo.ModeInfo.SegmentId = NegativeDeinterleave(reader.ReadSegmentId(ctx), predictor, lastActiveSegmentId + 1); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private int GetSegmentId(Av1PartitionInfo partitionInfo, int rowIndex, int columnIndex) |
|
|
|
|
|
{ |
|
|
|
|
|
int modeInfoOffset = (rowIndex * this.FrameInfo.ModeInfoColumnCount) + columnIndex; |
|
|
|
|
|
int bw4 = partitionInfo.ModeInfo.BlockSize.Get4x4WideCount(); |
|
|
|
|
|
int bh4 = partitionInfo.ModeInfo.BlockSize.Get4x4HighCount(); |
|
|
|
|
|
int xMin = Math.Min(this.FrameInfo.ModeInfoColumnCount - columnIndex, bw4); |
|
|
|
|
|
int yMin = Math.Min(this.FrameInfo.ModeInfoRowCount - rowIndex, bh4); |
|
|
|
|
|
int segmentId = ObuConstants.MaxSegments - 1; |
|
|
|
|
|
for (int y = 0; y < yMin; y++) |
|
|
|
|
|
{ |
|
|
|
|
|
for (int x = 0; x < xMin; x++) |
|
|
|
|
|
{ |
|
|
|
|
|
segmentId = Math.Min(segmentId, this.segmentIds[y][x]); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return segmentId; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private static int NegativeDeinterleave(int diff, int reference, int max) |
|
|
private static int NegativeDeinterleave(int diff, int reference, int max) |
|
|
@ -731,41 +755,45 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void ReadCdef(ref Av1SymbolDecoder reader, int rowIndex, int columnIndex, Av1BlockSize blockSize) |
|
|
private void ReadCdef(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
{ |
|
|
{ |
|
|
if (this.skip || this.FrameInfo.CodedLossless || !this.SequenceHeader.EnableCdef || this.FrameInfo.AllowIntraBlockCopy) |
|
|
if (partitionInfo.ModeInfo.Skip || this.FrameInfo.CodedLossless || !this.SequenceHeader.EnableCdef || this.FrameInfo.AllowIntraBlockCopy) |
|
|
{ |
|
|
{ |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
int cdefSize4 = Av1BlockSize.Block64x64.Get4x4WideCount(); |
|
|
int cdefSize4 = Av1BlockSize.Block64x64.Get4x4WideCount(); |
|
|
int cdefMask4 = ~(cdefSize4 - 1); |
|
|
int cdefMask4 = ~(cdefSize4 - 1); |
|
|
int r = rowIndex & cdefMask4; |
|
|
int r = partitionInfo.RowIndex & cdefMask4; |
|
|
int c = columnIndex & cdefMask4; |
|
|
int c = partitionInfo.ColumnIndex & cdefMask4; |
|
|
if (this.cdefIndex[r][c] == -1) |
|
|
if (partitionInfo.CdefStrength[r][c] == -1) |
|
|
{ |
|
|
{ |
|
|
this.cdefIndex[r][c] = reader.ReadLiteral(this.FrameInfo.CdefParameters.BitCount); |
|
|
partitionInfo.CdefStrength[r][c] = reader.ReadLiteral(this.FrameInfo.CdefParameters.BitCount); |
|
|
int w4 = blockSize.Get4x4WideCount(); |
|
|
if (this.SequenceHeader.SuperBlockSize == Av1BlockSize.Block128x128) |
|
|
int h4 = blockSize.Get4x4HighCount(); |
|
|
|
|
|
for (int i = r; i < r + h4; i += cdefSize4) |
|
|
|
|
|
{ |
|
|
{ |
|
|
for (int j = c; j < c + w4; j += cdefSize4) |
|
|
int w4 = partitionInfo.ModeInfo.BlockSize.Get4x4WideCount(); |
|
|
|
|
|
int h4 = partitionInfo.ModeInfo.BlockSize.Get4x4HighCount(); |
|
|
|
|
|
for (int i = r; i < r + h4; i += cdefSize4) |
|
|
{ |
|
|
{ |
|
|
this.cdefIndex[i][j] = this.cdefIndex[r][c]; |
|
|
for (int j = c; j < c + w4; j += cdefSize4) |
|
|
|
|
|
{ |
|
|
|
|
|
partitionInfo.CdefStrength[i & cdefMask4][j & cdefMask4] = partitionInfo.CdefStrength[r][c]; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void ReadDeltaLoopFilter(ref Av1SymbolDecoder reader, Av1BlockSize blockSize) |
|
|
private void ReadDeltaLoopFilter(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
{ |
|
|
{ |
|
|
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64; |
|
|
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64; |
|
|
if (blockSize == superBlockSize && this.skip) |
|
|
if (this.FrameInfo.DeltaLoopFilterParameters.IsPresent || |
|
|
|
|
|
(partitionInfo.ModeInfo.BlockSize == superBlockSize && partitionInfo.ModeInfo.Skip)) |
|
|
{ |
|
|
{ |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.readDeltas && this.FrameInfo.DeltaLoopFilterParameters.IsPresent) |
|
|
if (this.FrameInfo.DeltaLoopFilterParameters.IsPresent) |
|
|
{ |
|
|
{ |
|
|
int frameLoopFilterCount = 1; |
|
|
int frameLoopFilterCount = 1; |
|
|
if (this.FrameInfo.DeltaLoopFilterParameters.Multi) |
|
|
if (this.FrameInfo.DeltaLoopFilterParameters.Multi) |
|
|
@ -793,28 +821,32 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void ReadSkip(ref Av1SymbolDecoder reader) |
|
|
private bool ReadSkip(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
{ |
|
|
{ |
|
|
|
|
|
int segmentId = partitionInfo.ModeInfo.SegmentId; |
|
|
if (this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip && |
|
|
if (this.FrameInfo.SegmentationParameters.SegmentIdPrecedesSkip && |
|
|
this.FrameInfo.SegmentationParameters.IsFeatureActive(-1, ObuSegmentationFeature.LevelSkip)) |
|
|
this.FrameInfo.SegmentationParameters.IsFeatureActive(segmentId, ObuSegmentationLevelFeature.Skip)) |
|
|
{ |
|
|
{ |
|
|
this.skip = true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
this.skip = reader.ReadSkip(-1); |
|
|
int aboveSkip = partitionInfo.AboveModeInfo != null && partitionInfo.AboveModeInfo.Skip ? 1 : 0; |
|
|
|
|
|
int leftSkip = partitionInfo.LeftModeInfo != null && partitionInfo.LeftModeInfo.Skip ? 1 : 0; |
|
|
|
|
|
return reader.ReadSkip(aboveSkip + leftSkip); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private void ReadDeltaQuantizerIndex(ref Av1SymbolDecoder reader, Av1BlockSize blockSize) |
|
|
private void ReadDeltaQuantizerIndex(ref Av1SymbolDecoder reader, Av1PartitionInfo partitionInfo) |
|
|
{ |
|
|
{ |
|
|
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64; |
|
|
Av1BlockSize superBlockSize = this.SequenceHeader.Use128x128SuperBlock ? Av1BlockSize.Block128x128 : Av1BlockSize.Block64x64; |
|
|
if (blockSize == superBlockSize && this.skip) |
|
|
if (!this.FrameInfo.DeltaQParameters.IsPresent || |
|
|
|
|
|
(partitionInfo.ModeInfo.BlockSize == superBlockSize && partitionInfo.ModeInfo.Skip)) |
|
|
{ |
|
|
{ |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (this.readDeltas) |
|
|
if (partitionInfo.ModeInfo.BlockSize != this.SequenceHeader.SuperBlockSize || !partitionInfo.ModeInfo.Skip) |
|
|
{ |
|
|
{ |
|
|
int deltaQuantizerAbsolute = reader.ReadDeltaQuantizerAbsolute(); |
|
|
int deltaQuantizerAbsolute = reader.ReadDeltaQuantizerAbsolute(); |
|
|
if (deltaQuantizerAbsolute == ObuConstants.DeltaQuantizerSmall) |
|
|
if (deltaQuantizerAbsolute == ObuConstants.DeltaQuantizerSmall) |
|
|
@ -829,6 +861,7 @@ internal class Av1TileDecoder : IAv1TileDecoder |
|
|
bool deltaQuantizerSignBit = reader.ReadLiteral(1) > 0; |
|
|
bool deltaQuantizerSignBit = reader.ReadLiteral(1) > 0; |
|
|
int reducedDeltaQuantizerIndex = deltaQuantizerSignBit ? -deltaQuantizerAbsolute : deltaQuantizerAbsolute; |
|
|
int reducedDeltaQuantizerIndex = deltaQuantizerSignBit ? -deltaQuantizerAbsolute : deltaQuantizerAbsolute; |
|
|
this.currentQuantizerIndex = Av1Math.Clip3(1, 255, this.currentQuantizerIndex + (reducedDeltaQuantizerIndex << this.deltaQuantizerResolution)); |
|
|
this.currentQuantizerIndex = Av1Math.Clip3(1, 255, this.currentQuantizerIndex + (reducedDeltaQuantizerIndex << this.deltaQuantizerResolution)); |
|
|
|
|
|
partitionInfo.SuperblockInfo.SuperblockDeltaQ[0] = this.currentQuantizerIndex; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|