|
|
|
@ -22,7 +22,7 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
|
|
|
|
private readonly Av1Distribution tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy; |
|
|
|
private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes; |
|
|
|
private readonly Av1Distribution[] skip = Av1DefaultDistributions.Skip; |
|
|
|
private readonly Av1Distribution[][] transformBlockSkip; |
|
|
|
private readonly Av1Distribution[][][] endOfBlockFlag; |
|
|
|
private readonly Av1Distribution[][][] coefficientsBaseRange; |
|
|
|
private readonly Av1Distribution[][][] coefficientsBase; |
|
|
|
@ -36,6 +36,7 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
|
|
|
|
public Av1SymbolEncoder(Configuration configuration, int initialSize, int qIndex) |
|
|
|
{ |
|
|
|
this.transformBlockSkip = Av1DefaultDistributions.GetTransformBlockSkip(qIndex); |
|
|
|
this.endOfBlockFlag = Av1DefaultDistributions.GetEndOfBlockFlag(qIndex); |
|
|
|
this.coefficientsBaseRange = Av1DefaultDistributions.GetCoefficientsBaseRange(qIndex); |
|
|
|
this.coefficientsBase = Av1DefaultDistributions.GetCoefficientsBase(qIndex); |
|
|
|
@ -49,7 +50,7 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
public void WriteUseIntraBlockCopy(bool value) |
|
|
|
{ |
|
|
|
ref Av1SymbolWriter w = ref this.writer; |
|
|
|
w.WriteSymbol(value ? 1 : 0, this.tileIntraBlockCopy); |
|
|
|
w.WriteSymbol(value, this.tileIntraBlockCopy); |
|
|
|
} |
|
|
|
|
|
|
|
public void WritePartitionType(Av1PartitionType partitionType, int context) |
|
|
|
@ -82,10 +83,9 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
Av1TransformType transformType, |
|
|
|
int txbIndex, // TODO: Doesn't seem to be used, remove.
|
|
|
|
Av1PredictionMode intraDirection, |
|
|
|
Span<int> coeffBuffer, |
|
|
|
Span<int> coefficientBuffer, |
|
|
|
Av1ComponentType componentType, |
|
|
|
short transformBlockSkipContext, |
|
|
|
short dcSignContext, |
|
|
|
Av1TransformBlockContext transformBlockContext, |
|
|
|
ushort eob, |
|
|
|
bool useReducedTransformSet, |
|
|
|
int baseQIndex, |
|
|
|
@ -97,54 +97,53 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
Av1TransformClass transformClass = transformType.ToClass(); |
|
|
|
Av1ScanOrder scanOrder = Av1ScanOrderConstants.GetScanOrder(transformSize, transformType); |
|
|
|
ReadOnlySpan<short> scan = scanOrder.Scan; |
|
|
|
int bwl = transformSize.GetBlockWidthLog2(); |
|
|
|
int blockWidthLog2 = transformSize.GetBlockWidthLog2(); |
|
|
|
Av1TransformSize transformSizeContext = (Av1TransformSize)(((int)transformSize.GetSquareSize() + (int)transformSize.GetSquareUpSize() + 1) >> 1); |
|
|
|
|
|
|
|
ref Av1SymbolWriter w = ref this.writer; |
|
|
|
|
|
|
|
Av1LevelBuffer levels = new(this.configuration, new Size(width, height)); |
|
|
|
Span<sbyte> coeff_contexts = new sbyte[Av1Constants.MaxTransformSize * Av1Constants.MaxTransformSize]; |
|
|
|
Span<sbyte> coefficientContexts = new sbyte[Av1Constants.MaxTransformSize * Av1Constants.MaxTransformSize]; |
|
|
|
|
|
|
|
Guard.MustBeLessThan((int)transformSizeContext, (int)Av1TransformSize.AllSizes, nameof(transformSizeContext)); |
|
|
|
|
|
|
|
bool hasEndOfBlock = eob != 0; |
|
|
|
this.WriteSkip(!hasEndOfBlock, transformBlockSkipContext); |
|
|
|
this.WriteTransformBlockSkip(eob == 0, transformSizeContext, transformBlockContext.SkipContext); |
|
|
|
|
|
|
|
if (eob == 0) |
|
|
|
{ |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
levels.Initialize(coeffBuffer); |
|
|
|
levels.Initialize(coefficientBuffer); |
|
|
|
if (componentType == Av1ComponentType.Luminance) |
|
|
|
{ |
|
|
|
this.WriteTransformType(transformType, transformSize, useReducedTransformSet, baseQIndex, filterIntraMode, intraDirection); |
|
|
|
} |
|
|
|
|
|
|
|
short endOfBlockPosition = Av1SymbolContextHelper.GetEndOfBlockPosition(eob, out int eob_extra); |
|
|
|
short endOfBlockPosition = Av1SymbolContextHelper.GetEndOfBlockPosition(eob, out int eobExtra); |
|
|
|
this.WriteEndOfBlockFlag(componentType, transformClass, transformSize, endOfBlockPosition); |
|
|
|
|
|
|
|
int eob_offset_bits = Av1SymbolContextHelper.EndOfBlockOffsetBits[endOfBlockPosition]; |
|
|
|
if (eob_offset_bits > 0) |
|
|
|
int eobOffsetBitCount = Av1SymbolContextHelper.EndOfBlockOffsetBits[endOfBlockPosition]; |
|
|
|
if (eobOffsetBitCount > 0) |
|
|
|
{ |
|
|
|
int eob_shift = eob_offset_bits - 1; |
|
|
|
int bit = (eob_extra & (1 << eob_shift)) != 0 ? 1 : 0; |
|
|
|
int eobShift = eobOffsetBitCount - 1; |
|
|
|
int bit = (eobExtra & (1 << eobShift)) != 0 ? 1 : 0; |
|
|
|
w.WriteSymbol(bit, this.endOfBlockExtra[(int)transformSizeContext][(int)componentType][endOfBlockPosition]); |
|
|
|
for (int i = 1; i < eob_offset_bits; i++) |
|
|
|
for (int i = 1; i < eobOffsetBitCount; i++) |
|
|
|
{ |
|
|
|
eob_shift = eob_offset_bits - 1 - i; |
|
|
|
bit = (eob_extra & (1 << eob_shift)) != 0 ? 1 : 0; |
|
|
|
eobShift = eobOffsetBitCount - 1 - i; |
|
|
|
bit = (eobExtra & (1 << eobShift)) != 0 ? 1 : 0; |
|
|
|
w.WriteLiteral((uint)bit, 1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Av1SymbolContextHelper.GetNzMapContexts(levels, scan, eob, transformSize, transformClass, coeff_contexts); |
|
|
|
Av1SymbolContextHelper.GetNzMapContexts(levels, scan, eob, transformSize, transformClass, coefficientContexts); |
|
|
|
int limitedTransformSizeContext = Math.Min((int)transformSizeContext, (int)Av1TransformSize.Size32x32); |
|
|
|
for (c = eob - 1; c >= 0; --c) |
|
|
|
{ |
|
|
|
short pos = scan[c]; |
|
|
|
int v = coeffBuffer[pos]; |
|
|
|
short coeff_ctx = coeff_contexts[pos]; |
|
|
|
int v = coefficientBuffer[pos]; |
|
|
|
short coeff_ctx = coefficientContexts[pos]; |
|
|
|
int level = Math.Abs(v); |
|
|
|
|
|
|
|
if (c == eob - 1) |
|
|
|
@ -160,7 +159,7 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
{ |
|
|
|
// level is above 1.
|
|
|
|
int baseRange = level - 1 - Av1Constants.BaseLevelsCount; |
|
|
|
int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext(levels, pos, bwl, transformClass); |
|
|
|
int baseRangeContext = Av1SymbolContextHelper.GetBaseRangeContext(levels, pos, blockWidthLog2, transformClass); |
|
|
|
for (int idx = 0; idx < Av1Constants.CoefficientBaseRange; idx += Av1Constants.BaseRangeSizeMinus1) |
|
|
|
{ |
|
|
|
int k = Math.Min(baseRange - idx, Av1Constants.BaseRangeSizeMinus1); |
|
|
|
@ -179,7 +178,7 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
for (c = 0; c < eob; ++c) |
|
|
|
{ |
|
|
|
short pos = scan[c]; |
|
|
|
int v = coeffBuffer[pos]; |
|
|
|
int v = coefficientBuffer[pos]; |
|
|
|
int level = Math.Abs(v); |
|
|
|
cul_level += level; |
|
|
|
|
|
|
|
@ -188,7 +187,7 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
{ |
|
|
|
if (c == 0) |
|
|
|
{ |
|
|
|
w.WriteSymbol((int)sign, this.dcSign[(int)componentType][dcSignContext]); |
|
|
|
w.WriteSymbol((int)sign, this.dcSign[(int)componentType][transformBlockContext.DcSignContext]); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
@ -205,15 +204,14 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
cul_level = Math.Min(Av1Constants.CoefficientContextMask, cul_level); |
|
|
|
|
|
|
|
// DC value
|
|
|
|
Av1SymbolContextHelper.SetDcSign(ref cul_level, coeffBuffer[0]); |
|
|
|
Av1SymbolContextHelper.SetDcSign(ref cul_level, coefficientBuffer[0]); |
|
|
|
return cul_level; |
|
|
|
} |
|
|
|
|
|
|
|
private void WriteSkip(bool hasEndOfBlock, int context) |
|
|
|
internal void WriteTransformBlockSkip(bool skip, Av1TransformSize transformSizeContext, int skipContext) |
|
|
|
{ |
|
|
|
// Has EOB, means we won't skip, negating the logic.
|
|
|
|
ref Av1SymbolWriter w = ref this.writer; |
|
|
|
w.WriteSymbol(hasEndOfBlock ? 0 : 1, this.skip[context]); |
|
|
|
w.WriteSymbol(skip, this.transformBlockSkip[(int)transformSizeContext][skipContext]); |
|
|
|
} |
|
|
|
|
|
|
|
public IMemoryOwner<byte> Exit() |
|
|
|
@ -250,7 +248,7 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
|
|
|
|
for (int j = length - 1; j >= 0; --j) |
|
|
|
{ |
|
|
|
w.WriteLiteral((uint)(x >> j & 0x01), 1); |
|
|
|
w.WriteLiteral((uint)((x >> j) & 0x01), 1); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -265,7 +263,7 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
/// <summary>
|
|
|
|
/// SVT: av1_write_tx_type
|
|
|
|
/// </summary>
|
|
|
|
private void WriteTransformType( |
|
|
|
internal void WriteTransformType( |
|
|
|
Av1TransformType transformType, |
|
|
|
Av1TransformSize transformSize, |
|
|
|
bool useReducedTransformSet, |
|
|
|
@ -277,34 +275,32 @@ internal class Av1SymbolEncoder : IDisposable |
|
|
|
ref Av1SymbolWriter w = ref this.writer; |
|
|
|
if (Av1SymbolContextHelper.GetExtendedTransformTypeCount(transformSize, useReducedTransformSet) > 1 && baseQIndex > 0) |
|
|
|
{ |
|
|
|
Av1TransformSize square_tx_size = transformSize.GetSquareSize(); |
|
|
|
Guard.MustBeLessThanOrEqualTo((int)square_tx_size, Av1Constants.ExtendedTransformCount, nameof(square_tx_size)); |
|
|
|
Av1TransformSize squareTransformSize = transformSize.GetSquareSize(); |
|
|
|
Guard.MustBeLessThanOrEqualTo((int)squareTransformSize, Av1Constants.ExtendedTransformCount, nameof(squareTransformSize)); |
|
|
|
|
|
|
|
Av1TransformSetType tx_set_type = Av1SymbolContextHelper.GetExtendedTransformSetType(transformSize, useReducedTransformSet); |
|
|
|
int eset = Av1SymbolContextHelper.GetExtendedTransformSet(transformSize, useReducedTransformSet); |
|
|
|
Av1TransformSetType transformSetType = Av1SymbolContextHelper.GetExtendedTransformSetType(transformSize, useReducedTransformSet); |
|
|
|
int extendedSet = Av1SymbolContextHelper.GetExtendedTransformSet(transformSize, useReducedTransformSet); |
|
|
|
|
|
|
|
// eset == 0 should correspond to a set with only DCT_DCT and there
|
|
|
|
// is no need to send the tx_type
|
|
|
|
Guard.MustBeGreaterThan(eset, 0, nameof(eset)); |
|
|
|
Guard.MustBeGreaterThan(extendedSet, 0, nameof(extendedSet)); |
|
|
|
|
|
|
|
// assert(av1_ext_tx_used[tx_set_type][transformType]);
|
|
|
|
Av1PredictionMode intraMode; |
|
|
|
if (filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes) |
|
|
|
{ |
|
|
|
Av1PredictionMode intra_dir; |
|
|
|
if (filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes) |
|
|
|
{ |
|
|
|
intra_dir = filterIntraMode.ToIntraDirection(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
intra_dir = intraDirection; |
|
|
|
} |
|
|
|
|
|
|
|
Guard.MustBeLessThan((int)intra_dir, 13, nameof(intra_dir)); |
|
|
|
Guard.MustBeLessThan((int)square_tx_size, 4, nameof(square_tx_size)); |
|
|
|
w.WriteSymbol( |
|
|
|
ExtendedTransformIndices[(int)tx_set_type][(int)transformType], |
|
|
|
this.intraExtendedTransform[eset][(int)square_tx_size][(int)intra_dir]); |
|
|
|
intraMode = filterIntraMode.ToIntraDirection(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
intraMode = intraDirection; |
|
|
|
} |
|
|
|
|
|
|
|
Guard.MustBeLessThan((int)intraMode, 13, nameof(intraMode)); |
|
|
|
Guard.MustBeLessThan((int)squareTransformSize, 4, nameof(squareTransformSize)); |
|
|
|
w.WriteSymbol( |
|
|
|
ExtendedTransformIndices[(int)transformSetType][(int)transformType], |
|
|
|
this.intraExtendedTransform[extendedSet][(int)squareTransformSize][(int)intraMode]); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|