Browse Source

Initial implementation of Mode Info block writing

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
e2286243a6
  1. 6
      src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs
  2. 4
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs
  3. 10
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
  4. 84
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
  5. 2
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
  6. 1
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs
  7. 4
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs
  8. 2
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs
  9. 13
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs
  10. 42
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs
  11. 9
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ModeInfo.cs
  12. 2
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs
  13. 6
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs
  14. 45
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs
  15. 526
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs

6
src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs

@ -65,6 +65,12 @@ internal static class Av1BlockSizeExtensions
public static int Get4x4HighCount(this Av1BlockSize blockSize) => SizeHigh[(int)blockSize]; public static int Get4x4HighCount(this Av1BlockSize blockSize) => SizeHigh[(int)blockSize];
/// <summary>
/// Gets the <see cref="Av1BlockSize"/> given by the Log2 of the width and height.
/// </summary>
/// <param name="widthLog2">Log2 of the width value.</param>
/// <param name="heightLog2">Log2 of the height value.</param>
/// <returns>The <see cref="Av1BlockSize"/>.</returns>
public static Av1BlockSize FromWidthAndHeight(uint widthLog2, uint heightLog2) => HeightWidthToSize[heightLog2][widthLog2]; public static Av1BlockSize FromWidthAndHeight(uint widthLog2, uint heightLog2) => HeightWidthToSize[heightLog2][widthLog2];
/// <summary> /// <summary>

4
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs

@ -2003,9 +2003,9 @@ internal static class Av1DefaultDistributions
] ]
]; ];
public static Av1Distribution ChromeForLumaSign => new(1418, 2123, 13340, 18405, 26972, 28343, 32294); public static Av1Distribution ChromaFromLumaSign => new(1418, 2123, 13340, 18405, 26972, 28343, 32294);
public static Av1Distribution[] ChromeForLumaAlpha => public static Av1Distribution[] ChromaFromLumaAlpha =>
[ [
new(7637, 20719, 31401, 32481, 32657, 32688, 32692, 32696, 32700, 32704, 32708, 32712, 32716, 32720, 32724), new(7637, 20719, 31401, 32481, 32657, 32688, 32692, 32696, 32700, 32704, 32708, 32712, 32716, 32720, 32724),
new(14365, 23603, 28135, 31168, 32167, 32395, 32487, 32573, 32620, 32647, 32668, 32672, 32676, 32680, 32684), new(14365, 23603, 28135, 31168, 32167, 32395, 32487, 32573, 32620, 32647, 32668, 32672, 32676, 32680, 32684),

10
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs

@ -41,8 +41,8 @@ internal ref struct Av1SymbolDecoder
private readonly Av1Distribution[][][] coefficientsBaseRange; private readonly Av1Distribution[][][] coefficientsBaseRange;
private readonly Av1Distribution[][] transformBlockSkip; private readonly Av1Distribution[][] transformBlockSkip;
private readonly Av1Distribution[][][] endOfBlockExtra; private readonly Av1Distribution[][][] endOfBlockExtra;
private readonly Av1Distribution chromeForLumaSign = Av1DefaultDistributions.ChromeForLumaSign; private readonly Av1Distribution chromaFromLumaSign = Av1DefaultDistributions.ChromaFromLumaSign;
private readonly Av1Distribution[] chromeForLumaAlpha = Av1DefaultDistributions.ChromeForLumaAlpha; private readonly Av1Distribution[] chromaFromLumaAlpha = Av1DefaultDistributions.ChromaFromLumaAlpha;
private readonly Av1Distribution[][][] intraExtendedTransform = Av1DefaultDistributions.IntraExtendedTransform; private readonly Av1Distribution[][][] intraExtendedTransform = Av1DefaultDistributions.IntraExtendedTransform;
private readonly Configuration configuration; private readonly Configuration configuration;
private Av1SymbolReader reader; private Av1SymbolReader reader;
@ -248,21 +248,21 @@ internal ref struct Av1SymbolDecoder
public int ReadChromFromLumaSign() public int ReadChromFromLumaSign()
{ {
ref Av1SymbolReader r = ref this.reader; ref Av1SymbolReader r = ref this.reader;
return r.ReadSymbol(this.chromeForLumaSign); return r.ReadSymbol(this.chromaFromLumaSign);
} }
public int ReadChromaFromLumaAlphaU(int jointSignPlus1) public int ReadChromaFromLumaAlphaU(int jointSignPlus1)
{ {
ref Av1SymbolReader r = ref this.reader; ref Av1SymbolReader r = ref this.reader;
int context = jointSignPlus1 - 3; int context = jointSignPlus1 - 3;
return r.ReadSymbol(this.chromeForLumaAlpha[context]); return r.ReadSymbol(this.chromaFromLumaAlpha[context]);
} }
public int ReadChromaFromLumaAlphaV(int jointSignPlus1) public int ReadChromaFromLumaAlphaV(int jointSignPlus1)
{ {
ref Av1SymbolReader r = ref this.reader; ref Av1SymbolReader r = ref this.reader;
int context = AlphaVContexts[jointSignPlus1]; int context = AlphaVContexts[jointSignPlus1];
return r.ReadSymbol(this.chromeForLumaAlpha[context]); return r.ReadSymbol(this.chromaFromLumaAlpha[context]);
} }
/// <summary> /// <summary>

84
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs

@ -22,18 +22,25 @@ internal class Av1SymbolEncoder : IDisposable
private readonly Av1Distribution tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy; private readonly Av1Distribution tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy;
private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes; private readonly Av1Distribution[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes;
private readonly Av1Distribution[][] keyFrameYMode = Av1DefaultDistributions.KeyFrameYMode;
private readonly Av1Distribution[][] uvMode = Av1DefaultDistributions.UvMode;
private readonly Av1Distribution[][] transformBlockSkip; private readonly Av1Distribution[][] transformBlockSkip;
private readonly Av1Distribution[][][] endOfBlockFlag; private readonly Av1Distribution[][][] endOfBlockFlag;
private readonly Av1Distribution[][][] coefficientsBaseRange; private readonly Av1Distribution[][][] coefficientsBaseRange;
private readonly Av1Distribution[][][] coefficientsBase; private readonly Av1Distribution[][][] coefficientsBase;
private readonly Av1Distribution[][][] coefficientsBaseEndOfBlock; private readonly Av1Distribution[][][] coefficientsBaseEndOfBlock;
private readonly Av1Distribution[] filterIntra = Av1DefaultDistributions.FilterIntra;
private readonly Av1Distribution filterIntraMode = Av1DefaultDistributions.FilterIntraMode; private readonly Av1Distribution filterIntraMode = Av1DefaultDistributions.FilterIntraMode;
private readonly Av1Distribution deltaQuantizerAbsolute = Av1DefaultDistributions.DeltaQuantizerAbsolute;
private readonly Av1Distribution[][] dcSign; private readonly Av1Distribution[][] dcSign;
private readonly Av1Distribution[][][] endOfBlockExtra; private readonly Av1Distribution[][][] endOfBlockExtra;
private readonly Av1Distribution[][][] intraExtendedTransform = Av1DefaultDistributions.IntraExtendedTransform; private readonly Av1Distribution[][][] intraExtendedTransform = Av1DefaultDistributions.IntraExtendedTransform;
private readonly Av1Distribution[] segmentId = Av1DefaultDistributions.SegmentId; private readonly Av1Distribution[] segmentId = Av1DefaultDistributions.SegmentId;
private readonly Av1Distribution[] angleDelta = Av1DefaultDistributions.AngleDelta;
private readonly Av1Distribution[] skip = Av1DefaultDistributions.Skip; private readonly Av1Distribution[] skip = Av1DefaultDistributions.Skip;
private readonly Av1Distribution[] skipMode = Av1DefaultDistributions.SkipMode; private readonly Av1Distribution[] skipMode = Av1DefaultDistributions.SkipMode;
private readonly Av1Distribution chromaFromLumaSign = Av1DefaultDistributions.ChromaFromLumaSign;
private readonly Av1Distribution[] chromaFromLumaAlpha = Av1DefaultDistributions.ChromaFromLumaAlpha;
private bool isDisposed; private bool isDisposed;
private readonly Configuration configuration; private readonly Configuration configuration;
private Av1SymbolWriter writer; private Av1SymbolWriter writer;
@ -332,9 +339,86 @@ internal class Av1SymbolEncoder : IDisposable
w.WriteSymbol(skip, this.skipMode[context]); w.WriteSymbol(skip, this.skipMode[context]);
} }
internal void WriteFilterIntra(Av1FilterIntraMode filterIntraMode, Av1BlockSize blockSize)
{
ref Av1SymbolWriter w = ref this.writer;
w.WriteSymbol(filterIntraMode != Av1FilterIntraMode.AllFilterIntraModes, this.filterIntra[(int)blockSize]);
}
internal void WriteFilterIntraMode(Av1FilterIntraMode filterIntraMode) internal void WriteFilterIntraMode(Av1FilterIntraMode filterIntraMode)
{ {
ref Av1SymbolWriter w = ref this.writer; ref Av1SymbolWriter w = ref this.writer;
w.WriteSymbol((int)filterIntraMode, this.filterIntraMode); w.WriteSymbol((int)filterIntraMode, this.filterIntraMode);
} }
internal void WriteDeltaQIndex(int deltaQindex)
{
ref Av1SymbolWriter w = ref this.writer;
bool sign = deltaQindex < 0;
int abs = Math.Abs(deltaQindex);
bool smallval = abs < Av1Constants.DeltaQuantizerSmall;
w.WriteSymbol(Math.Min(abs, Av1Constants.DeltaQuantizerSmall), this.deltaQuantizerAbsolute);
if (!smallval)
{
int rem_bits = Av1Math.MostSignificantBit((uint)(abs - 1)) - 1;
int threshold = (1 << rem_bits) + 1;
w.WriteLiteral((uint)(rem_bits - 1), 3);
w.WriteLiteral((uint)(abs - threshold), rem_bits);
}
if (abs > 0)
{
w.WriteLiteral(sign);
}
}
internal void WriteLumaMode(Av1PredictionMode lumaMode, byte topContext, byte leftContext)
{
ref Av1SymbolWriter w = ref this.writer;
w.WriteSymbol((int)lumaMode, this.keyFrameYMode[topContext][leftContext]);
}
internal void WriteAngleDelta(int angleDelta, Av1PredictionMode context)
{
ref Av1SymbolWriter w = ref this.writer;
w.WriteSymbol(angleDelta, this.angleDelta[context - Av1PredictionMode.Vertical]);
}
internal void WriteCdefStrength(int cdefStrength, int bitCount)
{
ref Av1SymbolWriter w = ref this.writer;
w.WriteLiteral((uint)cdefStrength, bitCount);
}
internal void WriteChromaMode(Av1PredictionMode chromaMode, bool isChromaFromLumaAllowed, Av1PredictionMode lumaMode)
{
ref Av1SymbolWriter w = ref this.writer;
int cflAllowed = isChromaFromLumaAllowed ? 1 : 0;
w.WriteSymbol((int)chromaMode, this.uvMode[cflAllowed][(int)lumaMode]);
}
internal void WriteChromaFromLumaAlphas(int chromaFromLumaIndex, int joinedSign)
{
ref Av1SymbolWriter w = ref this.writer;
w.WriteSymbol(joinedSign, this.chromaFromLumaSign);
// Magnitudes are only signaled for nonzero codes.
int signU = ((joinedSign + 1) * 11) >> 5;
if (signU != 0)
{
int contextU = chromaFromLumaIndex - 2;
int indexU = chromaFromLumaIndex >> Av1Constants.ChromaFromLumaAlphabetSizeLog2;
w.WriteSymbol(indexU, this.chromaFromLumaAlpha[contextU]);
}
int signV = (joinedSign + 1) - (3 * signU);
if (signV != 0)
{
int contextV = (signV * 3) - signU - 3;
int indexV = chromaFromLumaIndex & ((1 << Av1Constants.ChromaFromLumaAlphabetSizeLog2) - 1);
w.WriteSymbol(indexV, this.chromaFromLumaAlpha[contextV]);
}
}
} }

2
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs

@ -38,6 +38,8 @@ internal class Av1SymbolWriter : IDisposable
distribution.Update(symbol); distribution.Update(symbol);
} }
public void WriteLiteral(bool value) => this.WriteLiteral(value ? 1u : 0u, 1);
public void WriteLiteral(uint value, int bitCount) public void WriteLiteral(uint value, int bitCount)
{ {
const uint p = 0x4000U; // (0x7FFFFFU - (128 << 15) + 128) >> 8; const uint p = 0x4000U; // (0x7FFFFFU - (128 << 15) + 128) >> 8;

1
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs

@ -23,5 +23,6 @@ internal enum Av1PredictionMode
IntraModeStart = DC, IntraModeStart = DC,
IntraModeEnd = Paeth + 1, IntraModeEnd = Paeth + 1,
IntraModes = Paeth, IntraModes = Paeth,
UvIntraModes = UvChromaFromLuma + 1,
IntraInvalid = 25, IntraInvalid = 25,
} }

4
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs

@ -24,4 +24,8 @@ internal class Av1EncoderBlockModeInfo
public int SegmentId { get; } public int SegmentId { get; }
public int TransformDepth { get; internal set; } public int TransformDepth { get; internal set; }
public Av1PredictionMode Mode { get; internal set; }
public Av1PredictionMode UvMode { get; internal set; }
} }

2
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs

@ -18,4 +18,6 @@ internal class Av1EncoderBlockStruct
public Av1FilterIntraMode FilterIntraMode { get; set; } public Av1FilterIntraMode FilterIntraMode { get; set; }
public required int[] PaletteSize { get; internal set; } public required int[] PaletteSize { get; internal set; }
public required Av1EncoderPredictionUnit[] PredictionUnits { get; internal set; }
} }

13
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs

@ -0,0 +1,13 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal class Av1EncoderPredictionUnit
{
public required byte[] AngleDelta { get; set; }
public int ChromaFromLumaIndex { get; internal set; }
public int ChromaFromLumaSigns { get; internal set; }
}

42
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs

@ -5,15 +5,51 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal class Av1MacroBlockD internal class Av1MacroBlockD
{ {
public required Av1BlockModeInfo ModeInfo { get; internal set; } private Av1ModeInfo[] modeInfo = [];
public required Span<Av1ModeInfo> ModeInfo
{
get => this.modeInfo;
internal set
{
this.modeInfo = new Av1ModeInfo[value.Length];
value.CopyTo(this.modeInfo);
}
}
public required Av1TileInfo Tile { get; internal set; } public required Av1TileInfo Tile { get; internal set; }
public bool IsUpAvailable { get; } public bool IsUpAvailable { get; internal set; }
public bool IsLeftAvailable { get; } public bool IsLeftAvailable { get; internal set; }
public Av1MacroBlockModeInfo? AboveMacroBlock { get; internal set; } public Av1MacroBlockModeInfo? AboveMacroBlock { get; internal set; }
public Av1MacroBlockModeInfo? LeftMacroBlock { get; internal set; } public Av1MacroBlockModeInfo? LeftMacroBlock { get; internal set; }
public int ModeInfoStride { get; internal set; }
/// <summary>
/// Gets or sets the number of macro blocks until the top edge.
/// </summary>
public int ToTopEdge { get; internal set; }
/// <summary>
/// Gets or sets the number of macro blocks until the bottom edge.
/// </summary>
public int ToBottomEdge { get; internal set; }
/// <summary>
/// Gets or sets the number of macro blocks until the left edge.
/// </summary>
public int ToLeftEdge { get; internal set; }
/// <summary>
/// Gets or sets the number of macro blocks until the right edge.
/// </summary>
public int ToRightEdge { get; internal set; }
public Size N8Size { get; internal set; }
public bool IsSecondRectangle { get; internal set; }
} }

9
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ModeInfo.cs

@ -0,0 +1,9 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal class Av1ModeInfo
{
public required Av1MacroBlockModeInfo MacroBlockModeInfo { get; internal set; }
}

2
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs

@ -165,4 +165,6 @@ internal class Av1NeighborArrayUnit<T>
} }
} }
} }
internal void UnitModeWrite(Span<byte> dcSignSpan, Point blockOrigin, Size blockSize, Av1NeighborArrayUnit<Av1PartitionContext>.UnitMask unitMask) => throw new NotImplementedException();
} }

6
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs

@ -19,6 +19,12 @@ internal struct Av1PartitionContext : IMinMaxValue<Av1PartitionContext>
// Mask to extract ModeInfo offset within max ModeInfoBlock // Mask to extract ModeInfo offset within max ModeInfoBlock
public const int Mask = (1 << (7 - 2)) - 1; public const int Mask = (1 << (7 - 2)) - 1;
public Av1PartitionContext(byte above, byte left)
{
this.Above = above;
this.Left = left;
}
public static Av1PartitionContext MaxValue => throw new NotImplementedException(); public static Av1PartitionContext MaxValue => throw new NotImplementedException();
public static Av1PartitionContext MinValue => throw new NotImplementedException(); public static Av1PartitionContext MinValue => throw new NotImplementedException();

45
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal class Av1PictureControlSet internal class Av1PictureControlSet
@ -21,7 +23,48 @@ internal class Av1PictureControlSet
public required byte[] SegmentationNeighborMap { get; internal set; } public required byte[] SegmentationNeighborMap { get; internal set; }
public required Av1BlockModeInfo[] ModeInfoGrid { get; internal set; } public Av1ModeInfo[][] ModeInfoGrid { get; } = [];
public required Av1ModeInfo[] Mip { get; internal set; }
public int ModeInfoStride { get; internal set; }
// true if 4x4 blocks are disallowed for all frames, and NSQ is disabled (since granularity is
// needed for 8x8 NSQ blocks). Used to compute the offset for mip.
public bool Disallow4x4AllFrames { get; internal set; }
public required int[][] CdefPreset { get; internal set; }
public Span<Av1ModeInfo> GetFromModeInfoGrid(Point position)
=> this.ModeInfoGrid[(position.Y * this.ModeInfoStride) + position.X];
public void SetModeInfoGridRow(Point position, Span<Av1ModeInfo> span)
=> this.SetModeInfoGridRow((position.Y * this.ModeInfoStride) + position.X, span);
public void SetModeInfoGridRow(int offset, Span<Av1ModeInfo> span)
{
this.ModeInfoGrid[offset] = new Av1ModeInfo[span.Length];
span.CopyTo(this.ModeInfoGrid[offset]);
}
/// <summary>
/// SVT: get_mbmi
/// </summary>
internal Av1MacroBlockModeInfo GetMacroBlockModeInfo(Point blockOrigin)
{
int modeInfoStride = this.ModeInfoStride;
int offset = (blockOrigin.Y * modeInfoStride) + blockOrigin.X;
// Reset the mi_grid (needs to be done here in case it was changed for NSQ blocks during MD - svt_aom_init_xd())
// mip offset may be different from grid offset when 4x4 blocks are disallowed
int disallow4x4 = this.Disallow4x4AllFrames ? 1 : 0;
int mipOffset = ((blockOrigin.Y >> disallow4x4) * (modeInfoStride >> disallow4x4)) + (blockOrigin.X >> disallow4x4);
this.SetModeInfoGridRow(offset, ((Span<Av1ModeInfo>)this.Mip)[mipOffset..]);
// use idx 0 as that's the first MacroBlockModeInfo in the block.
Av1ModeInfo modeInfo = this.ModeInfoGrid[offset][0];
return modeInfo.MacroBlockModeInfo;
}
/// <summary> /// <summary>
/// SVT: svt_av1_update_segmentation_map /// SVT: svt_av1_update_segmentation_map

526
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs

@ -12,6 +12,37 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal partial class Av1TileWriter internal partial class Av1TileWriter
{ {
// Generates 5 bit field in which each bit set to 1 represents
// a BlockSize partition 11111 means we split 128x128, 64x64, 32x32, 16x16
// and 8x8. 10000 means we just split the 128x128 to 64x64
private static readonly Av1PartitionContext[] PartitionContextLookup =
[
new(31, 31), // 4X4 - {0b11111, 0b11111}
new(31, 30), // 4X8 - {0b11111, 0b11110}
new(30, 31), // 8X4 - {0b11110, 0b11111}
new(30, 30), // 8X8 - {0b11110, 0b11110}
new(30, 28), // 8X16 - {0b11110, 0b11100}
new(28, 30), // 16X8 - {0b11100, 0b11110}
new(28, 28), // 16X16 - {0b11100, 0b11100}
new(28, 24), // 16X32 - {0b11100, 0b11000}
new(24, 28), // 32X16 - {0b11000, 0b11100}
new(24, 24), // 32X32 - {0b11000, 0b11000}
new(24, 16), // 32X64 - {0b11000, 0b10000}
new(16, 24), // 64X32 - {0b10000, 0b11000}
new(16, 16), // 64X64 - {0b10000, 0b10000}
new(16, 0), // 64X128- {0b10000, 0b00000}
new(0, 16), // 128X64- {0b00000, 0b10000}
new(0, 0), // 128X128-{0b00000, 0b00000}
new(31, 28), // 4X16 - {0b11111, 0b11100}
new(28, 31), // 16X4 - {0b11100, 0b11111}
new(30, 24), // 8X32 - {0b11110, 0b11000}
new(24, 30), // 32X8 - {0b11000, 0b11110}
new(28, 16), // 16X64 - {0b11100, 0b10000}
new(16, 28), // 64X16 - {0b10000, 0b11100}
];
private static readonly byte[] IntraModeContextLookup = [0, 1, 2, 3, 4, 4, 4, 4, 3, 0, 1, 2, 0];
/// <summary> /// <summary>
/// SVT: svt_aom_write_sb /// SVT: svt_aom_write_sb
/// </summary> /// </summary>
@ -24,11 +55,11 @@ internal partial class Av1TileWriter
ushort tileIndex) ushort tileIndex)
{ {
Av1SequenceControlSet scs = pcs.Sequence; Av1SequenceControlSet scs = pcs.Sequence;
Av1NeighborArrayUnit<Av1PartitionContext> partition_context_na = pcs.PartitionContexts[tileIndex]; Av1NeighborArrayUnit<Av1PartitionContext> partitionContextNeighbors = pcs.PartitionContexts[tileIndex];
// CU Varaiables // CU Varaiables
int blk_index = 0; int blockIndex = 0;
uint final_blk_index = 0; uint finalBlockIndex = 0;
ec_ctx.CodedAreaSuperblock = 0; ec_ctx.CodedAreaSuperblock = 0;
ec_ctx.CodedAreaSuperblockUv = 0; ec_ctx.CodedAreaSuperblockUv = 0;
@ -37,8 +68,8 @@ internal partial class Av1TileWriter
do do
{ {
bool code_blk_cond = true; // Code cu only if it is inside the picture bool code_blk_cond = true; // Code cu only if it is inside the picture
Av1EncoderBlockStruct blk_ptr = superblock.FinalBlocks[final_blk_index]; Av1EncoderBlockStruct blk_ptr = superblock.FinalBlocks[finalBlockIndex];
Av1BlockGeometry blk_geom = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blk_index); Av1BlockGeometry blk_geom = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blockIndex);
Av1BlockSize bsize = blk_geom.BlockSize; Av1BlockSize bsize = blk_geom.BlockSize;
Point blockOrigin = blk_geom.Origin; Point blockOrigin = blk_geom.Origin;
@ -98,17 +129,16 @@ internal partial class Av1TileWriter
// Code Split Flag // Code Split Flag
EncodePartition( EncodePartition(
pcs, pcs,
ec_ctx, ref writer,
writer,
bsize, bsize,
superblock.CodingUnitPartitionTypes[blk_index], superblock.CodingUnitPartitionTypes[blockIndex],
blockOrigin, blockOrigin,
partition_context_na); partitionContextNeighbors);
} }
// assert(blk_geom.Shape == PART_N); // assert(blk_geom.Shape == PART_N);
Guard.IsTrue(Av1Math.Implies(bsize == Av1BlockSize.Block4x4, superblock.CodingUnitPartitionTypes[blk_index] == Av1PartitionType.None), nameof(bsize), string.Empty); Guard.IsTrue(Av1Math.Implies(bsize == Av1BlockSize.Block4x4, superblock.CodingUnitPartitionTypes[blockIndex] == Av1PartitionType.None), nameof(bsize), string.Empty);
switch (superblock.CodingUnitPartitionTypes[blk_index]) switch (superblock.CodingUnitPartitionTypes[blockIndex])
{ {
case Av1PartitionType.None: case Av1PartitionType.None:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
@ -119,8 +149,8 @@ internal partial class Av1TileWriter
if (mi_row + hbs < cm.ModeInfoRowCount) if (mi_row + hbs < cm.ModeInfoRowCount)
{ {
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
} }
@ -130,8 +160,8 @@ internal partial class Av1TileWriter
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
if (mi_col + hbs < cm.ModeInfoColumnCount) if (mi_col + hbs < cm.ModeInfoColumnCount)
{ {
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
} }
@ -141,48 +171,48 @@ internal partial class Av1TileWriter
case Av1PartitionType.HorizontalA: case Av1PartitionType.HorizontalA:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
break; break;
case Av1PartitionType.HorizontalB: case Av1PartitionType.HorizontalB:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
break; break;
case Av1PartitionType.VerticalA: case Av1PartitionType.VerticalA:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
break; break;
case Av1PartitionType.VerticalB: case Av1PartitionType.VerticalB:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
break; break;
@ -200,8 +230,8 @@ internal partial class Av1TileWriter
if (i > 0) if (i > 0)
{ {
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
} }
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
@ -222,8 +252,8 @@ internal partial class Av1TileWriter
if (i > 0) if (i > 0)
{ {
final_blk_index++; finalBlockIndex++;
blk_ptr = superblock.FinalBlocks[final_blk_index]; blk_ptr = superblock.FinalBlocks[finalBlockIndex];
} }
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer); WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
@ -232,26 +262,24 @@ internal partial class Av1TileWriter
break; break;
} }
if (superblock.CodingUnitPartitionTypes[blk_index] != Av1PartitionType.Split) if (superblock.CodingUnitPartitionTypes[blockIndex] != Av1PartitionType.Split)
{ {
final_blk_index++; finalBlockIndex++;
blk_index += blk_geom.NextDepthOffset; blockIndex += blk_geom.NextDepthOffset;
} }
else else
{ {
blk_index += blk_geom.Depth1Offset; blockIndex += blk_geom.Depth1Offset;
} }
} }
else else
{ {
blk_index += blk_geom.Depth1Offset; blockIndex += blk_geom.Depth1Offset;
} }
} }
while (blk_index < scs.MaxBlockCount); while (blockIndex < scs.MaxBlockCount);
} }
private static void EncodePartition(Av1PictureControlSet pcs, Av1EntropyCodingContext ec_ctx, Av1SymbolEncoder writer, Av1BlockSize bsize, object value, Point blockOrigin, Av1NeighborArrayUnit<Av1PartitionContext> partition_context_na) => throw new NotImplementedException();
/// <summary> /// <summary>
/// SVT: encode_partition_av1 /// SVT: encode_partition_av1
/// </summary> /// </summary>
@ -332,15 +360,14 @@ internal partial class Av1TileWriter
{ {
Av1SequenceControlSet scs = pcs.Sequence; Av1SequenceControlSet scs = pcs.Sequence;
ObuFrameHeader frm_hdr = pcs.Parent.FrameHeader; ObuFrameHeader frm_hdr = pcs.Parent.FrameHeader;
/*
Av1NeighborArrayUnit<byte> luma_dc_sign_level_coeff_na = pcs.LuminanceDcSignLevelCoefficientNeighbors[tile_idx]; Av1NeighborArrayUnit<byte> luma_dc_sign_level_coeff_na = pcs.LuminanceDcSignLevelCoefficientNeighbors[tile_idx];
Av1NeighborArrayUnit<byte> cr_dc_sign_level_coeff_na = pcs.CrDcSignLevelCoefficientNeighbors[tile_idx]; Av1NeighborArrayUnit<byte> cr_dc_sign_level_coeff_na = pcs.CrDcSignLevelCoefficientNeighbors[tile_idx];
Av1NeighborArrayUnit<byte> cb_dc_sign_level_coeff_na = pcs.CbDcSignLevelCoefficientNeighbors[tile_idx]; Av1NeighborArrayUnit<byte> cb_dc_sign_level_coeff_na = pcs.CbDcSignLevelCoefficientNeighbors[tile_idx];
Av1NeighborArrayUnit<byte> txfm_context_array = pcs.TransformFunctionContexts[tile_idx]; Av1NeighborArrayUnit<byte> txfm_context_array = pcs.TransformFunctionContexts[tile_idx];
Av1BlockGeometry blockGeometry = GetBlockGeometryMds(blk_ptr.ModeDecisionScanIndex); Av1BlockGeometry blockGeometry = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blk_ptr.ModeDecisionScanIndex);
Point blockOrigin = Point.Add(entropyCodingContext.SuperblockOrigin, (Size)blockGeometry.Origin); Point blockOrigin = Point.Add(entropyCodingContext.SuperblockOrigin, (Size)blockGeometry.Origin);
Av1BlockSize blockSize = blockGeometry.BlockSize; Av1BlockSize blockSize = blockGeometry.BlockSize;
Av1MacroBlockModeInfo macroBlockModeInfo = GetMacroBlockModeInfo(pcs, blockOrigin); Av1MacroBlockModeInfo macroBlockModeInfo = pcs.GetMacroBlockModeInfo(blockOrigin);
bool skipWritingCoefficients = macroBlockModeInfo.Block.Skip; bool skipWritingCoefficients = macroBlockModeInfo.Block.Skip;
entropyCodingContext.MacroBlockModeInfo = macroBlockModeInfo; entropyCodingContext.MacroBlockModeInfo = macroBlockModeInfo;
@ -359,7 +386,7 @@ internal partial class Av1TileWriter
if (blk_ptr.MacroBlock.IsUpAvailable) if (blk_ptr.MacroBlock.IsUpAvailable)
{ {
blk_ptr.MacroBlock.AboveMacroBlock = blk_ptr.MacroBlock.ModeInfo[-mi_stride].mbmi; blk_ptr.MacroBlock.AboveMacroBlock = blk_ptr.MacroBlock.ModeInfo[-mi_stride].MacroBlockModeInfo;
} }
else else
{ {
@ -368,25 +395,21 @@ internal partial class Av1TileWriter
if (blk_ptr.MacroBlock.IsLeftAvailable) if (blk_ptr.MacroBlock.IsLeftAvailable)
{ {
blk_ptr.MacroBlock.LeftMacroBlock = blk_ptr.MacroBlock.ModeInfo[-1].mbmi; blk_ptr.MacroBlock.LeftMacroBlock = blk_ptr.MacroBlock.ModeInfo[-1].MacroBlockModeInfo;
} }
else else
{ {
blk_ptr.MacroBlock.LeftMacroBlock = null; blk_ptr.MacroBlock.LeftMacroBlock = null;
} }
blk_ptr.MacroBlock.tile_ctx = frame_context; // Not required, part of Av1SymbolEncoder.
// blk_ptr.MacroBlock.tile_ctx = frame_context;
int bw = blockSize.GetWidth(); SetModeInfoRowAndColumn(
int bh = blockSize.GetHeight();
set_mi_row_col(
pcs, pcs,
blk_ptr.MacroBlock, blk_ptr.MacroBlock,
blk_ptr.MacroBlock.Tile, blk_ptr.MacroBlock.Tile,
mi_row, modeInfoPosition,
bh, blockSize,
mi_col,
bw,
mi_stride, mi_stride,
pcs.Parent.Common.ModeInfoRowCount, pcs.Parent.Common.ModeInfoRowCount,
pcs.Parent.Common.ModeInfoColumnCount); pcs.Parent.Common.ModeInfoColumnCount);
@ -394,7 +417,6 @@ internal partial class Av1TileWriter
// if (pcs.slice_type == I_SLICE) // if (pcs.slice_type == I_SLICE)
// We implement only INTRA frames. // We implement only INTRA frames.
{ {
// const int32_t skip = write_skip(cm, xd, mbmi->segment_id, mi, w) // const int32_t skip = write_skip(cm, xd, mbmi->segment_id, mi, w)
if (pcs.Parent.FrameHeader.SegmentationParameters.Enabled && pcs.Parent.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip) if (pcs.Parent.FrameHeader.SegmentationParameters.Enabled && pcs.Parent.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip)
{ {
@ -413,7 +435,6 @@ internal partial class Av1TileWriter
pcs, pcs,
ref writer, ref writer,
tile_idx, tile_idx,
blk_ptr.MacroBlock,
skipWritingCoefficients, skipWritingCoefficients,
blockOrigin << Av1Constants.ModeInfoSizeLog2); blockOrigin << Av1Constants.ModeInfoSizeLog2);
@ -434,8 +455,8 @@ internal partial class Av1TileWriter
} }
Av1PredictionMode intra_luma_mode = macroBlockModeInfo.Block.Mode; Av1PredictionMode intra_luma_mode = macroBlockModeInfo.Block.Mode;
uint intra_chroma_mode = macroBlockModeInfo.Block.UvMode; Av1PredictionMode intra_chroma_mode = macroBlockModeInfo.Block.UvMode;
if (svt_aom_allow_intrabc(pcs.Parent.FrameHeader, pcs.Parent.SliceType)) if (IsIntraBlockCopyAllowed(pcs.Parent.FrameHeader/*, pcs.Parent.SliceType*/))
{ {
WriteIntraBlockCopyInfo(ref writer, macroBlockModeInfo, blk_ptr); WriteIntraBlockCopyInfo(ref writer, macroBlockModeInfo, blk_ptr);
} }
@ -460,10 +481,10 @@ internal partial class Av1TileWriter
} }
} }
if (!macroBlockModeInfo.Block.UseIntraBlockCopy && svt_aom_allow_palette(frm_hdr.AllowScreenContentTools, blockGeometry.BlockSize)) if (!macroBlockModeInfo.Block.UseIntraBlockCopy && IsPaletteAllowed(frm_hdr.AllowScreenContentTools, blockGeometry.BlockSize))
{ {
WritePaletteModeInfo( WritePaletteModeInfo(
pcs.Parent, scs,
ref writer, ref writer,
macroBlockModeInfo, macroBlockModeInfo,
blk_ptr, blk_ptr,
@ -472,10 +493,9 @@ internal partial class Av1TileWriter
} }
if (!macroBlockModeInfo.Block.UseIntraBlockCopy && if (!macroBlockModeInfo.Block.UseIntraBlockCopy &&
svt_aom_filter_intra_allowed( IsFilterIntraAllowed(scs.SequenceHeader.FilterIntraLevel > 0, blockSize, blk_ptr.PaletteSize[0], intra_luma_mode))
scs.SequenceHeader.FilterIntraLevel, blockSize, blk_ptr.PaletteSize[0], intra_luma_mode))
{ {
writer.WriteSkip(blk_ptr.FilterIntraMode != Av1FilterIntraMode.AllFilterIntraModes, blockSize); writer.WriteFilterIntra(blk_ptr.FilterIntraMode, blockSize);
if (blk_ptr.FilterIntraMode != Av1FilterIntraMode.AllFilterIntraModes) if (blk_ptr.FilterIntraMode != Av1FilterIntraMode.AllFilterIntraModes)
{ {
writer.WriteFilterIntraMode(blk_ptr.FilterIntraMode); writer.WriteFilterIntraMode(blk_ptr.FilterIntraMode);
@ -484,13 +504,16 @@ internal partial class Av1TileWriter
if (!macroBlockModeInfo.Block.UseIntraBlockCopy) if (!macroBlockModeInfo.Block.UseIntraBlockCopy)
{ {
assert(blk_ptr.PaletteSize[1] == 0); Guard.IsTrue(blk_ptr.PaletteSize[1] == 0, nameof(blk_ptr), "Palette of chroma plane shall be empty.");
TOKENEXTRA tok = entropyCodingContext.tok;
// TOKENEXTRA tok = entropyCodingContext.tok;
for (int plane = 0; plane < 2; ++plane) for (int plane = 0; plane < 2; ++plane)
{ {
int palette_size_plane = blk_ptr.PaletteSize[plane]; int palette_size_plane = blk_ptr.PaletteSize[plane];
if (palette_size_plane > 0) if (palette_size_plane > 0)
{ {
throw new NotImplementedException("Tokenizing palette not implemented.");
/*
Av1TransformSize tx_size = Av1TransformSize tx_size =
blockGeometry.TransformSize[macroBlockModeInfo.Block.TransformDepth]; // inherit tx_size from 1st transform block; blockGeometry.TransformSize[macroBlockModeInfo.Block.TransformDepth]; // inherit tx_size from 1st transform block;
svt_av1_tokenize_color_map( svt_av1_tokenize_color_map(
@ -503,12 +526,13 @@ internal partial class Av1TileWriter
PALETTE_MAP, PALETTE_MAP,
0); // NO CDF update in entropy, the update will take place in arithmetic encode 0); // NO CDF update in entropy, the update will take place in arithmetic encode
assert(macroBlockModeInfo.Block.UseIntraBlockCopy); assert(macroBlockModeInfo.Block.UseIntraBlockCopy);
assert(svt_aom_allow_palette(pcs.Parent.FrameHeader.AllowScreenContentTools, blockGeometry.BlockSize)); assert(IsPaletteAllowed(pcs.Parent.FrameHeader.AllowScreenContentTools, blockGeometry.BlockSize));
svt_aom_get_block_dimensions(blockGeometry.BlockSize, plane, blk_ptr.MacroBlock, null, null, out int rowCount, out int columnCount); svt_aom_get_block_dimensions(blockGeometry.BlockSize, plane, blk_ptr.MacroBlock, null, null, out int rowCount, out int columnCount);
pack_map_tokens(ref writer, ref entropyCodingContext.tok, palette_size_plane, rowCount * columnCount); pack_map_tokens(ref writer, ref entropyCodingContext.tok, palette_size_plane, rowCount * columnCount);
// advance the pointer // advance the pointer
entropyCodingContext.tok = tok; entropyCodingContext.tok = tok;
*/
} }
} }
} }
@ -546,16 +570,374 @@ internal partial class Av1TileWriter
} }
// Update the neighbors // Update the neighbors
ec_update_neighbors(pcs, entropyCodingContext, blockOrigin, blk_ptr, tile_idx, blockSize, coeff_ptr); UpdateNeighbors(pcs, entropyCodingContext, blockOrigin, blk_ptr, tile_idx, blockSize);
if (svt_av1_allow_palette(pcs.Parent.PaletteLevel, blockGeometry.BlockSize)) if (IsPaletteAllowed(pcs.Parent.PaletteLevel, blockGeometry.BlockSize))
{ {
/*
// free ENCDEC palette info buffer // free ENCDEC palette info buffer
assert(blk_ptr.palette_info.color_idx_map != null && "free palette:Null"); assert(blk_ptr.palette_info.color_idx_map != null && "free palette:Null");
EB_FREE(blk_ptr.palette_info.color_idx_map); EB_FREE(blk_ptr.palette_info.color_idx_map);
blk_ptr.palette_info.color_idx_map = null; blk_ptr.palette_info.color_idx_map = null;
EB_FREE(blk_ptr.palette_info); EB_FREE(blk_ptr.palette_info);*/
}
}
private static void EncodeIntraChromaMode(
ref Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo,
Av1EncoderBlockStruct blk_ptr,
Av1BlockSize blockSize,
Av1PredictionMode lumaMode,
Av1PredictionMode chromaMode,
bool isChromaFromLumaAllowed)
{
writer.WriteChromaMode(chromaMode, isChromaFromLumaAllowed, lumaMode);
if (chromaMode == Av1PredictionMode.UvChromaFromLuma)
{
writer.WriteChromaFromLumaAlphas(
blk_ptr.PredictionUnits[0].ChromaFromLumaIndex,
blk_ptr.PredictionUnits[0].ChromaFromLumaSigns);
}
if (blockSize >= Av1BlockSize.Block8x8 && macroBlockModeInfo.Block.UvMode.IsDirectional())
{
writer.WriteAngleDelta(blk_ptr.PredictionUnits[0].AngleDelta[(int)Av1PlaneType.Uv] + Av1Constants.MaxAngleDelta, chromaMode);
}
}
/// <summary>
/// Get the contexts (left and top) for writing the intra luma mode for key frames.
/// Intended to be used for key frame only.
/// </summary>
/// <remarks>SVT: svt_aom_get_kf_y_mode_ctx</remarks>
private static void GetYModeContext(Av1MacroBlockD xd, out byte above_ctx, out byte left_ctx)
{
Av1PredictionMode intraLumaLeftMode = Av1PredictionMode.DC;
Av1PredictionMode intraLumaTopMode = Av1PredictionMode.DC;
if (xd.IsLeftAvailable)
{
// When called for key frame, neighbouring mode should be intra
// assert(!is_inter_block(&xd->mi[-1]->mbmi.block_mi) || is_intrabc_block(&xd->mi[-1]->mbmi.block_mi));
intraLumaLeftMode = xd.ModeInfo[-1].MacroBlockModeInfo.Block.Mode;
}
if (xd.IsUpAvailable)
{
// When called for key frame, neighbouring mode should be intra
// assert(!is_inter_block(&xd->mi[-xd->mi_stride]->mbmi.block_mi) ||
// is_intrabc_block(&xd->mi[-xd->mi_stride]->mbmi.block_mi));
intraLumaTopMode = xd.ModeInfo[-xd.ModeInfoStride].MacroBlockModeInfo.Block.Mode;
}
above_ctx = IntraModeContextLookup[(int)intraLumaTopMode];
left_ctx = IntraModeContextLookup[(int)intraLumaLeftMode];
}
/// <summary>
/// SVT: encode_intra_luma_mode_kf_av1
/// </summary>
private static void EncodeIntraLumaMode(
ref Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo,
Av1EncoderBlockStruct blk_ptr,
Av1BlockSize blockSize,
Av1PredictionMode lumaMode)
{
GetYModeContext(blk_ptr.MacroBlock, out byte topContext, out byte leftContext);
writer.WriteLumaMode(lumaMode, topContext, leftContext);
if (blockSize >= Av1BlockSize.Block8x8 && macroBlockModeInfo.Block.Mode.IsDirectional())
{
writer.WriteAngleDelta(blk_ptr.PredictionUnits[0].AngleDelta[(int)Av1PlaneType.Y] + Av1Constants.MaxAngleDelta, lumaMode);
}
}
private static void WritePaletteModeInfo(
Av1SequenceControlSet scs,
ref Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo,
Av1EncoderBlockStruct blk_ptr,
Av1BlockSize blockSize,
Point point)
{/*
Av1PredictionMode intra_luma_mode = macroBlockModeInfo.Mode;
Av1PredictionMode intra_chroma_mode = macroBlockModeInfo.ModeUv;
Av1PaletteModeInfo pmi = blk_ptr.PaletteInfo.pmi;
int bsize_ctx = svt_aom_get_palette_bsize_ctx(bsize);
Guard.MustBeGreaterThanOrEqualTo(bsize_ctx, 0, nameof(bsize_ctx));
if (intra_luma_mode == Av1PredictionMode.DC)
{
int n = blk_ptr.PaletteSize[0];
int palette_y_mode_ctx = svt_aom_get_palette_mode_ctx(blk_ptr->av1xd);
writer.WriteYMode(n > 0, bsize_ctx, palette_y_mode_ctx);
if (n > 0)
{
writer.WriteYSize(n - PALETTE_MIN_SIZE, bsize_ctx);
write_palette_colors_y(blk_ptr.MacroBlock, pmi, scs.StaticConfig.EncoderBitDepth, ref writer, n);
}
}
bool uv_dc_pred = intra_chroma_mode == Av1PredictionMode.DC && is_chroma_reference(point, blockSize, 1, 1);
if (uv_dc_pred)
{
// assert(blk_ptr->palette_size[1] == 0); //remove when chroma is on
bool palette_uv_mode_ctx = blk_ptr.PaletteSize[0] > 0;
writer.WriteUvMode(false, palette_uv_mode_ctx);
}*/ }*/
throw new NotImplementedException("Palette mode encoding not implemented.");
}
/// <summary>
/// SVT: svt_aom_filter_intra_allowed
/// </summary>
private static bool IsFilterIntraAllowed(
bool enableFilterIntra,
Av1BlockSize blockSize,
int paletteSize,
Av1PredictionMode mode)
=> mode == Av1PredictionMode.DC && paletteSize == 0 && IsFilterIntraAllowedBlockSize(enableFilterIntra, blockSize);
/// <summary>
/// SVT: svt_aom_filter_intra_allowed_bsize
/// </summary>
private static bool IsFilterIntraAllowedBlockSize(bool enableFilterIntra, Av1BlockSize blockSize)
{
if (!enableFilterIntra)
{
return false;
}
return blockSize.GetWidth() <= 32 && blockSize.GetHeight() <= 32;
}
/// <summary>
/// SVT: write_intrabc_info
/// </summary>
private static void WriteIntraBlockCopyInfo(
ref Av1SymbolEncoder writer,
Av1MacroBlockModeInfo macroBlockModeInfo,
Av1EncoderBlockStruct block)
{
bool use_intrabc = macroBlockModeInfo.Block.UseIntraBlockCopy;
writer.WriteUseIntraBlockCopy(use_intrabc);
if (use_intrabc)
{
throw new NotImplementedException("Intra block code encoding not implemented.");
/*
//assert(mbmi->mode == DC_PRED);
//assert(mbmi->uv_mode == UV_DC_PRED);
//assert(mbmi->motion_mode == SIMPLE_TRANSLATION);
IntMv dv_ref = block->predmv[0]; // mbmi_ext->ref_mv_stack[INTRA_FRAME][0].this_mv;
MV mv;
mv = macroBlockModeInfo.Block.mv[INTRA_FRAME].as_mv;
svt_av1_encode_dv(w, &mv, &dv_ref.as_mv, &ec_ctx->ndvc);*/
}
}
/// <summary>
/// SVT: svt_aom_allow_intrabc
/// </summary>
private static bool IsIntraBlockCopyAllowed(ObuFrameHeader frameHeader)
=> frameHeader.AllowScreenContentTools && frameHeader.AllowIntraBlockCopy;
/// <summary>
/// SVT: ec_update_neighbors
/// </summary>
private static void UpdateNeighbors(
Av1PictureControlSet pcs,
Av1EntropyCodingContext entropyCodingContext,
Point blockOrigin,
Av1EncoderBlockStruct blk_ptr,
ushort tile_idx,
Av1BlockSize blockSize)
{
Av1NeighborArrayUnit<Av1PartitionContext> partition_context_na = pcs.PartitionContexts[tile_idx];
Av1NeighborArrayUnit<byte> luma_dc_sign_level_coeff_na = pcs.LuminanceDcSignLevelCoefficientNeighbors[tile_idx];
Av1NeighborArrayUnit<byte> cr_dc_sign_level_coeff_na = pcs.CrDcSignLevelCoefficientNeighbors[tile_idx];
Av1NeighborArrayUnit<byte> cb_dc_sign_level_coeff_na = pcs.CbDcSignLevelCoefficientNeighbors[tile_idx];
Av1BlockGeometry blk_geom = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blk_ptr.ModeDecisionScanIndex);
Av1MacroBlockModeInfo mbmi = pcs.GetMacroBlockModeInfo(blockOrigin);
bool skip_coeff = mbmi.Block.Skip;
// Update the Leaf Depth Neighbor Array
Av1PartitionContext partition = new(
PartitionContextLookup[(int)blockSize].Above,
PartitionContextLookup[(int)blockSize].Left);
Size size = new(blk_geom.BlockWidth, blk_geom.BlockHeight);
Span<Av1PartitionContext> partitionSpan = new(ref partition);
partition_context_na.UnitModeWrite(
partitionSpan,
blockOrigin,
size,
Av1NeighborArrayUnit<Av1PartitionContext>.UnitMask.Left | Av1NeighborArrayUnit<Av1PartitionContext>.UnitMask.Top);
if (skip_coeff)
{
byte dcSignLevelCoefficient = 0;
Span<byte> dcSignSpan = new(ref dcSignLevelCoefficient);
luma_dc_sign_level_coeff_na.UnitModeWrite(
dcSignSpan,
blockOrigin,
size,
Av1NeighborArrayUnit<Av1PartitionContext>.UnitMask.Left | Av1NeighborArrayUnit<Av1PartitionContext>.UnitMask.Top);
if (blk_geom.HasUv)
{
cb_dc_sign_level_coeff_na.UnitModeWrite(
dcSignSpan,
((blockOrigin >> 3) << 3) >> 1,
size,
Av1NeighborArrayUnit<Av1PartitionContext>.UnitMask.Left | Av1NeighborArrayUnit<Av1PartitionContext>.UnitMask.Top);
cr_dc_sign_level_coeff_na.UnitModeWrite(
dcSignSpan,
((blockOrigin >> 3) << 3) >> 1,
size,
Av1NeighborArrayUnit<Av1PartitionContext>.UnitMask.Left | Av1NeighborArrayUnit<Av1PartitionContext>.UnitMask.Top);
entropyCodingContext.CodedAreaSuperblockUv += blk_geom.BlockWidthUv * blk_geom.BlockHeightUv;
}
entropyCodingContext.CodedAreaSuperblock += blk_geom.BlockWidth * blk_geom.BlockHeight;
}
}
/// <summary>
/// SVT: svt_av1_allow_palette
/// </summary>
private static bool IsPaletteAllowed(int allowPalette, Av1BlockSize blockSize)
{
Guard.MustBeLessThan((int)blockSize, (int)Av1BlockSize.AllSizes, nameof(blockSize));
return allowPalette != 0 &&
blockSize.GetWidth() <= 64 &&
blockSize.GetHeight() <= 64 &&
blockSize >= Av1BlockSize.Block8x8;
}
/// <summary>
/// SVT: svt_aom_allow_palette
/// </summary>
private static bool IsPaletteAllowed(bool allowScreenContentTools, Av1BlockSize blockSize)
=> allowScreenContentTools &&
blockSize.GetWidth() <= 64 &&
blockSize.GetHeight() <= 64 &&
blockSize >= Av1BlockSize.Block8x8;
/// <summary>
/// SVT: write_cdef
/// </summary>
private static void WriteCdef(
Av1SequenceControlSet scs,
Av1PictureControlSet pcs,
ref Av1SymbolEncoder writer,
int tileIndex,
bool skip,
Point modeInfoPosition)
{
Av1EncoderCommon cm = pcs.Parent.Common;
ObuFrameHeader frameHeader = pcs.Parent.FrameHeader;
if (frameHeader.CodedLossless || frameHeader.AllowIntraBlockCopy)
{
// Initialize to indicate no CDEF for safety.
frameHeader.CdefParameters.BitCount = 0;
frameHeader.CdefParameters.YStrength[0] = 0;
frameHeader.CdefParameters.UvStrength[0] = 0;
// pcs.Parent.nb_cdef_strengths = 1;
return;
}
// int m = ~((1 << (6 - Av1Constants.ModeInfoSizeLog2)) - 1);
// cm->mi_grid_visible[(mi_row & m) * cm->mi_stride + (mi_col & m)];
Av1ModeInfo mi = pcs.GetFromModeInfoGrid(modeInfoPosition)[0];
// Initialise when at top left part of the superblock
if ((modeInfoPosition.Y & (scs.SequenceHeader.SuperblockModeInfoSize - 1)) == 0 &&
(modeInfoPosition.X & (scs.SequenceHeader.SuperblockModeInfoSize - 1)) == 0)
{
// Top left?
pcs.CdefPreset[tileIndex][0] = -1;
pcs.CdefPreset[tileIndex][1] = -1;
pcs.CdefPreset[tileIndex][2] = -1;
pcs.CdefPreset[tileIndex][3] = -1;
}
// Emit CDEF param at first non-skip coding block
int mask = 1 << (6 - Av1Constants.ModeInfoSizeLog2);
int index = scs.SequenceHeader.Use128x128Superblock ? Math.Max(1, modeInfoPosition.X & mask) + (2 * Math.Max(1, modeInfoPosition.Y & mask)) : 0;
if (pcs.CdefPreset[tileIndex][index] == -1 && !skip)
{
writer.WriteCdefStrength(mi.MacroBlockModeInfo.CdefStrength, frameHeader.CdefParameters.BitCount);
pcs.CdefPreset[tileIndex][index] = mi.MacroBlockModeInfo.CdefStrength;
}
}
/// <summary>
/// SVT: set_mi_row_col
/// </summary>
private static void SetModeInfoRowAndColumn(
Av1PictureControlSet pcs,
Av1MacroBlockD macroBlock,
Av1TileInfo tile,
Point modeInfoPosition,
Av1BlockSize blockSize,
int modeInfoStride,
int modeInfoRowCount,
int modeInfoColumnCount)
{
macroBlock.ToTopEdge = -((modeInfoPosition.Y << Av1Constants.ModeInfoSizeLog2) << 3);
macroBlock.ToBottomEdge = ((modeInfoRowCount - blockSize.GetHeight() - modeInfoPosition.Y) << Av1Constants.ModeInfoSizeLog2) << 3;
macroBlock.ToLeftEdge = -((modeInfoPosition.X << Av1Constants.ModeInfoSizeLog2) << 3);
macroBlock.ToRightEdge = ((modeInfoColumnCount - blockSize.GetWidth() - modeInfoPosition.X) << Av1Constants.ModeInfoSizeLog2) << 3;
macroBlock.ModeInfoStride = modeInfoStride;
// Are edges available for intra prediction?
macroBlock.IsUpAvailable = modeInfoPosition.Y > tile.ModeInfoRowStart;
macroBlock.IsLeftAvailable = modeInfoPosition.X > tile.ModeInfoColumnStart;
macroBlock.ModeInfo = pcs.GetFromModeInfoGrid(modeInfoPosition);
if (macroBlock.IsUpAvailable)
{
macroBlock.AboveMacroBlock = macroBlock.ModeInfo[-modeInfoStride].MacroBlockModeInfo;
}
else
{
macroBlock.AboveMacroBlock = null;
}
if (macroBlock.IsLeftAvailable)
{
macroBlock.LeftMacroBlock = macroBlock.ModeInfo[-1].MacroBlockModeInfo;
}
else
{
macroBlock.LeftMacroBlock = null;
}
macroBlock.N8Size = new Size(blockSize.GetWidth(), blockSize.GetHeight());
macroBlock.IsSecondRectangle = false;
if (macroBlock.N8Size.Width < macroBlock.N8Size.Height)
{
// Only mark is_sec_rect as 1 for the last block.
// For PARTITION_VERT_4, it would be (0, 0, 0, 1);
// For other partitions, it would be (0, 1).
if (((modeInfoPosition.X + macroBlock.N8Size.Width) & (macroBlock.N8Size.Height - 1)) == 0)
{
macroBlock.IsSecondRectangle = true;
}
}
if (macroBlock.N8Size.Width > macroBlock.N8Size.Height)
{
if ((modeInfoPosition.Y & (macroBlock.N8Size.Width - 1)) > 0)
{
macroBlock.IsSecondRectangle = true;
}
}
} }
/// <summary> /// <summary>

Loading…
Cancel
Save