diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs
index 1863ed8dec..df5f533806 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs
+++ b/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];
+ ///
+ /// Gets the given by the Log2 of the width and height.
+ ///
+ /// Log2 of the width value.
+ /// Log2 of the height value.
+ /// The .
public static Av1BlockSize FromWidthAndHeight(uint widthLog2, uint heightLog2) => HeightWidthToSize[heightLog2][widthLog2];
///
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs
index 743708fc2b..cc08556fdd 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1DefaultDistributions.cs
+++ b/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(14365, 23603, 28135, 31168, 32167, 32395, 32487, 32573, 32620, 32647, 32668, 32672, 32676, 32680, 32684),
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
index f96833fde0..e74b905c2a 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolDecoder.cs
@@ -41,8 +41,8 @@ internal ref struct Av1SymbolDecoder
private readonly Av1Distribution[][][] coefficientsBaseRange;
private readonly Av1Distribution[][] transformBlockSkip;
private readonly Av1Distribution[][][] endOfBlockExtra;
- private readonly Av1Distribution chromeForLumaSign = Av1DefaultDistributions.ChromeForLumaSign;
- private readonly Av1Distribution[] chromeForLumaAlpha = Av1DefaultDistributions.ChromeForLumaAlpha;
+ private readonly Av1Distribution chromaFromLumaSign = Av1DefaultDistributions.ChromaFromLumaSign;
+ private readonly Av1Distribution[] chromaFromLumaAlpha = Av1DefaultDistributions.ChromaFromLumaAlpha;
private readonly Av1Distribution[][][] intraExtendedTransform = Av1DefaultDistributions.IntraExtendedTransform;
private readonly Configuration configuration;
private Av1SymbolReader reader;
@@ -248,21 +248,21 @@ public bool ReadTransformBlockSkip(Av1TransformSize transformSizeContext, int sk
public int ReadChromFromLumaSign()
{
ref Av1SymbolReader r = ref this.reader;
- return r.ReadSymbol(this.chromeForLumaSign);
+ return r.ReadSymbol(this.chromaFromLumaSign);
}
public int ReadChromaFromLumaAlphaU(int jointSignPlus1)
{
ref Av1SymbolReader r = ref this.reader;
int context = jointSignPlus1 - 3;
- return r.ReadSymbol(this.chromeForLumaAlpha[context]);
+ return r.ReadSymbol(this.chromaFromLumaAlpha[context]);
}
public int ReadChromaFromLumaAlphaV(int jointSignPlus1)
{
ref Av1SymbolReader r = ref this.reader;
int context = AlphaVContexts[jointSignPlus1];
- return r.ReadSymbol(this.chromeForLumaAlpha[context]);
+ return r.ReadSymbol(this.chromaFromLumaAlpha[context]);
}
///
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
index 3425c3d000..72db8b02e7 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
+++ b/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[] tilePartitionTypes = Av1DefaultDistributions.PartitionTypes;
+ private readonly Av1Distribution[][] keyFrameYMode = Av1DefaultDistributions.KeyFrameYMode;
+ private readonly Av1Distribution[][] uvMode = Av1DefaultDistributions.UvMode;
private readonly Av1Distribution[][] transformBlockSkip;
private readonly Av1Distribution[][][] endOfBlockFlag;
private readonly Av1Distribution[][][] coefficientsBaseRange;
private readonly Av1Distribution[][][] coefficientsBase;
private readonly Av1Distribution[][][] coefficientsBaseEndOfBlock;
+ private readonly Av1Distribution[] filterIntra = Av1DefaultDistributions.FilterIntra;
private readonly Av1Distribution filterIntraMode = Av1DefaultDistributions.FilterIntraMode;
+ private readonly Av1Distribution deltaQuantizerAbsolute = Av1DefaultDistributions.DeltaQuantizerAbsolute;
private readonly Av1Distribution[][] dcSign;
private readonly Av1Distribution[][][] endOfBlockExtra;
private readonly Av1Distribution[][][] intraExtendedTransform = Av1DefaultDistributions.IntraExtendedTransform;
private readonly Av1Distribution[] segmentId = Av1DefaultDistributions.SegmentId;
+ private readonly Av1Distribution[] angleDelta = Av1DefaultDistributions.AngleDelta;
private readonly Av1Distribution[] skip = Av1DefaultDistributions.Skip;
private readonly Av1Distribution[] skipMode = Av1DefaultDistributions.SkipMode;
+ private readonly Av1Distribution chromaFromLumaSign = Av1DefaultDistributions.ChromaFromLumaSign;
+ private readonly Av1Distribution[] chromaFromLumaAlpha = Av1DefaultDistributions.ChromaFromLumaAlpha;
private bool isDisposed;
private readonly Configuration configuration;
private Av1SymbolWriter writer;
@@ -332,9 +339,86 @@ internal void WriteSkipMode(bool skip, int 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)
{
ref Av1SymbolWriter w = ref this.writer;
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]);
+ }
+ }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
index c5e31ded60..5c2702ad3c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
@@ -38,6 +38,8 @@ public void WriteSymbol(int symbol, Av1Distribution distribution)
distribution.Update(symbol);
}
+ public void WriteLiteral(bool value) => this.WriteLiteral(value ? 1u : 0u, 1);
+
public void WriteLiteral(uint value, int bitCount)
{
const uint p = 0x4000U; // (0x7FFFFFU - (128 << 15) + 128) >> 8;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs
index dc35c96f31..86d9e96c77 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Prediction/Av1PredictionMode.cs
@@ -23,5 +23,6 @@ internal enum Av1PredictionMode
IntraModeStart = DC,
IntraModeEnd = Paeth + 1,
IntraModes = Paeth,
+ UvIntraModes = UvChromaFromLuma + 1,
IntraInvalid = 25,
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs
index 54db0c3cd5..482b5c26ba 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockModeInfo.cs
@@ -24,4 +24,8 @@ internal class Av1EncoderBlockModeInfo
public int SegmentId { get; }
public int TransformDepth { get; internal set; }
+
+ public Av1PredictionMode Mode { get; internal set; }
+
+ public Av1PredictionMode UvMode { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs
index 45bf6603c1..cd988c96e1 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderBlockStruct.cs
@@ -18,4 +18,6 @@ internal class Av1EncoderBlockStruct
public Av1FilterIntraMode FilterIntraMode { get; set; }
public required int[] PaletteSize { get; internal set; }
+
+ public required Av1EncoderPredictionUnit[] PredictionUnits { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPredictionUnit.cs
new file mode 100644
index 0000000000..5b46389ef9
--- /dev/null
+++ b/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; }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs
index 62dd3972bb..bb69f9ee8c 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1MacroBlockD.cs
@@ -5,15 +5,51 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal class Av1MacroBlockD
{
- public required Av1BlockModeInfo ModeInfo { get; internal set; }
+ private Av1ModeInfo[] modeInfo = [];
+
+ public required Span ModeInfo
+ {
+ get => this.modeInfo;
+ internal set
+ {
+ this.modeInfo = new Av1ModeInfo[value.Length];
+ value.CopyTo(this.modeInfo);
+ }
+ }
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? LeftMacroBlock { get; internal set; }
+
+ public int ModeInfoStride { get; internal set; }
+
+ ///
+ /// Gets or sets the number of macro blocks until the top edge.
+ ///
+ public int ToTopEdge { get; internal set; }
+
+ ///
+ /// Gets or sets the number of macro blocks until the bottom edge.
+ ///
+ public int ToBottomEdge { get; internal set; }
+
+ ///
+ /// Gets or sets the number of macro blocks until the left edge.
+ ///
+ public int ToLeftEdge { get; internal set; }
+
+ ///
+ /// Gets or sets the number of macro blocks until the right edge.
+ ///
+ public int ToRightEdge { get; internal set; }
+
+ public Size N8Size { get; internal set; }
+
+ public bool IsSecondRectangle { get; internal set; }
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ModeInfo.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1ModeInfo.cs
new file mode 100644
index 0000000000..ba6f3b0843
--- /dev/null
+++ b/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; }
+}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs
index 4592a3066f..f4e2957a51 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1NeighborArrayUnit.cs
@@ -165,4 +165,6 @@ public void UnitModeWrite(ReadOnlySpan value, Point origin, Size blockSize, U
}
}
}
+
+ internal void UnitModeWrite(Span dcSignSpan, Point blockOrigin, Size blockSize, Av1NeighborArrayUnit.UnitMask unitMask) => throw new NotImplementedException();
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs
index 2d9c910d01..73b3d67570 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PartitionContext.cs
@@ -19,6 +19,12 @@ internal struct Av1PartitionContext : IMinMaxValue
// Mask to extract ModeInfo offset within max ModeInfoBlock
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 MinValue => throw new NotImplementedException();
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs
index fe156b68e0..7e6978edaf 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1PictureControlSet.cs
@@ -1,6 +1,8 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
+using System;
+
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
internal class Av1PictureControlSet
@@ -21,7 +23,48 @@ internal class Av1PictureControlSet
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 GetFromModeInfoGrid(Point position)
+ => this.ModeInfoGrid[(position.Y * this.ModeInfoStride) + position.X];
+
+ public void SetModeInfoGridRow(Point position, Span span)
+ => this.SetModeInfoGridRow((position.Y * this.ModeInfoStride) + position.X, span);
+
+ public void SetModeInfoGridRow(int offset, Span span)
+ {
+ this.ModeInfoGrid[offset] = new Av1ModeInfo[span.Length];
+ span.CopyTo(this.ModeInfoGrid[offset]);
+ }
+
+ ///
+ /// SVT: get_mbmi
+ ///
+ 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)this.Mip)[mipOffset..]);
+
+ // use idx 0 as that's the first MacroBlockModeInfo in the block.
+ Av1ModeInfo modeInfo = this.ModeInfoGrid[offset][0];
+ return modeInfo.MacroBlockModeInfo;
+ }
///
/// SVT: svt_av1_update_segmentation_map
diff --git a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
index b5885f7774..c405cbd054 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
@@ -12,6 +12,37 @@ namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
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];
+
///
/// SVT: svt_aom_write_sb
///
@@ -24,11 +55,11 @@ public static void WriteSuperblock(
ushort tileIndex)
{
Av1SequenceControlSet scs = pcs.Sequence;
- Av1NeighborArrayUnit partition_context_na = pcs.PartitionContexts[tileIndex];
+ Av1NeighborArrayUnit partitionContextNeighbors = pcs.PartitionContexts[tileIndex];
// CU Varaiables
- int blk_index = 0;
- uint final_blk_index = 0;
+ int blockIndex = 0;
+ uint finalBlockIndex = 0;
ec_ctx.CodedAreaSuperblock = 0;
ec_ctx.CodedAreaSuperblockUv = 0;
@@ -37,8 +68,8 @@ public static void WriteSuperblock(
do
{
bool code_blk_cond = true; // Code cu only if it is inside the picture
- Av1EncoderBlockStruct blk_ptr = superblock.FinalBlocks[final_blk_index];
- Av1BlockGeometry blk_geom = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blk_index);
+ Av1EncoderBlockStruct blk_ptr = superblock.FinalBlocks[finalBlockIndex];
+ Av1BlockGeometry blk_geom = Av1BlockGeometryFactory.GetBlockGeometryByModeDecisionScanIndex(blockIndex);
Av1BlockSize bsize = blk_geom.BlockSize;
Point blockOrigin = blk_geom.Origin;
@@ -98,17 +129,16 @@ public static void WriteSuperblock(
// Code Split Flag
EncodePartition(
pcs,
- ec_ctx,
- writer,
+ ref writer,
bsize,
- superblock.CodingUnitPartitionTypes[blk_index],
+ superblock.CodingUnitPartitionTypes[blockIndex],
blockOrigin,
- partition_context_na);
+ partitionContextNeighbors);
}
// assert(blk_geom.Shape == PART_N);
- Guard.IsTrue(Av1Math.Implies(bsize == Av1BlockSize.Block4x4, superblock.CodingUnitPartitionTypes[blk_index] == Av1PartitionType.None), nameof(bsize), string.Empty);
- switch (superblock.CodingUnitPartitionTypes[blk_index])
+ Guard.IsTrue(Av1Math.Implies(bsize == Av1BlockSize.Block4x4, superblock.CodingUnitPartitionTypes[blockIndex] == Av1PartitionType.None), nameof(bsize), string.Empty);
+ switch (superblock.CodingUnitPartitionTypes[blockIndex])
{
case Av1PartitionType.None:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
@@ -119,8 +149,8 @@ public static void WriteSuperblock(
if (mi_row + hbs < cm.ModeInfoRowCount)
{
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
}
@@ -130,8 +160,8 @@ public static void WriteSuperblock(
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
if (mi_col + hbs < cm.ModeInfoColumnCount)
{
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
}
@@ -141,48 +171,48 @@ public static void WriteSuperblock(
case Av1PartitionType.HorizontalA:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
break;
case Av1PartitionType.HorizontalB:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
break;
case Av1PartitionType.VerticalA:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
break;
case Av1PartitionType.VerticalB:
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
break;
@@ -200,8 +230,8 @@ public static void WriteSuperblock(
if (i > 0)
{
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
}
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
@@ -222,8 +252,8 @@ public static void WriteSuperblock(
if (i > 0)
{
- final_blk_index++;
- blk_ptr = superblock.FinalBlocks[final_blk_index];
+ finalBlockIndex++;
+ blk_ptr = superblock.FinalBlocks[finalBlockIndex];
}
WriteModesBlock(pcs, ec_ctx, ref writer, superblock, blk_ptr, tileIndex, frameBuffer);
@@ -232,26 +262,24 @@ public static void WriteSuperblock(
break;
}
- if (superblock.CodingUnitPartitionTypes[blk_index] != Av1PartitionType.Split)
+ if (superblock.CodingUnitPartitionTypes[blockIndex] != Av1PartitionType.Split)
{
- final_blk_index++;
- blk_index += blk_geom.NextDepthOffset;
+ finalBlockIndex++;
+ blockIndex += blk_geom.NextDepthOffset;
}
else
{
- blk_index += blk_geom.Depth1Offset;
+ blockIndex += blk_geom.Depth1Offset;
}
}
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 partition_context_na) => throw new NotImplementedException();
-
///
/// SVT: encode_partition_av1
///
@@ -332,15 +360,14 @@ private static void WriteModesBlock(
{
Av1SequenceControlSet scs = pcs.Sequence;
ObuFrameHeader frm_hdr = pcs.Parent.FrameHeader;
- /*
Av1NeighborArrayUnit luma_dc_sign_level_coeff_na = pcs.LuminanceDcSignLevelCoefficientNeighbors[tile_idx];
Av1NeighborArrayUnit cr_dc_sign_level_coeff_na = pcs.CrDcSignLevelCoefficientNeighbors[tile_idx];
Av1NeighborArrayUnit cb_dc_sign_level_coeff_na = pcs.CbDcSignLevelCoefficientNeighbors[tile_idx];
Av1NeighborArrayUnit 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);
Av1BlockSize blockSize = blockGeometry.BlockSize;
- Av1MacroBlockModeInfo macroBlockModeInfo = GetMacroBlockModeInfo(pcs, blockOrigin);
+ Av1MacroBlockModeInfo macroBlockModeInfo = pcs.GetMacroBlockModeInfo(blockOrigin);
bool skipWritingCoefficients = macroBlockModeInfo.Block.Skip;
entropyCodingContext.MacroBlockModeInfo = macroBlockModeInfo;
@@ -359,7 +386,7 @@ private static void WriteModesBlock(
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
{
@@ -368,25 +395,21 @@ private static void WriteModesBlock(
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
{
blk_ptr.MacroBlock.LeftMacroBlock = null;
}
- blk_ptr.MacroBlock.tile_ctx = frame_context;
-
- int bw = blockSize.GetWidth();
- int bh = blockSize.GetHeight();
- set_mi_row_col(
+ // Not required, part of Av1SymbolEncoder.
+ // blk_ptr.MacroBlock.tile_ctx = frame_context;
+ SetModeInfoRowAndColumn(
pcs,
blk_ptr.MacroBlock,
blk_ptr.MacroBlock.Tile,
- mi_row,
- bh,
- mi_col,
- bw,
+ modeInfoPosition,
+ blockSize,
mi_stride,
pcs.Parent.Common.ModeInfoRowCount,
pcs.Parent.Common.ModeInfoColumnCount);
@@ -394,7 +417,6 @@ private static void WriteModesBlock(
// if (pcs.slice_type == I_SLICE)
// We implement only INTRA frames.
{
-
// const int32_t skip = write_skip(cm, xd, mbmi->segment_id, mi, w)
if (pcs.Parent.FrameHeader.SegmentationParameters.Enabled && pcs.Parent.FrameHeader.SegmentationParameters.SegmentIdPrecedesSkip)
{
@@ -413,7 +435,6 @@ private static void WriteModesBlock(
pcs,
ref writer,
tile_idx,
- blk_ptr.MacroBlock,
skipWritingCoefficients,
blockOrigin << Av1Constants.ModeInfoSizeLog2);
@@ -434,8 +455,8 @@ private static void WriteModesBlock(
}
Av1PredictionMode intra_luma_mode = macroBlockModeInfo.Block.Mode;
- uint intra_chroma_mode = macroBlockModeInfo.Block.UvMode;
- if (svt_aom_allow_intrabc(pcs.Parent.FrameHeader, pcs.Parent.SliceType))
+ Av1PredictionMode intra_chroma_mode = macroBlockModeInfo.Block.UvMode;
+ if (IsIntraBlockCopyAllowed(pcs.Parent.FrameHeader/*, pcs.Parent.SliceType*/))
{
WriteIntraBlockCopyInfo(ref writer, macroBlockModeInfo, blk_ptr);
}
@@ -460,10 +481,10 @@ private static void WriteModesBlock(
}
}
- if (!macroBlockModeInfo.Block.UseIntraBlockCopy && svt_aom_allow_palette(frm_hdr.AllowScreenContentTools, blockGeometry.BlockSize))
+ if (!macroBlockModeInfo.Block.UseIntraBlockCopy && IsPaletteAllowed(frm_hdr.AllowScreenContentTools, blockGeometry.BlockSize))
{
WritePaletteModeInfo(
- pcs.Parent,
+ scs,
ref writer,
macroBlockModeInfo,
blk_ptr,
@@ -472,10 +493,9 @@ private static void WriteModesBlock(
}
if (!macroBlockModeInfo.Block.UseIntraBlockCopy &&
- svt_aom_filter_intra_allowed(
- scs.SequenceHeader.FilterIntraLevel, blockSize, blk_ptr.PaletteSize[0], intra_luma_mode))
+ IsFilterIntraAllowed(scs.SequenceHeader.FilterIntraLevel > 0, 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)
{
writer.WriteFilterIntraMode(blk_ptr.FilterIntraMode);
@@ -484,13 +504,16 @@ private static void WriteModesBlock(
if (!macroBlockModeInfo.Block.UseIntraBlockCopy)
{
- assert(blk_ptr.PaletteSize[1] == 0);
- TOKENEXTRA tok = entropyCodingContext.tok;
+ Guard.IsTrue(blk_ptr.PaletteSize[1] == 0, nameof(blk_ptr), "Palette of chroma plane shall be empty.");
+
+ // TOKENEXTRA tok = entropyCodingContext.tok;
for (int plane = 0; plane < 2; ++plane)
{
int palette_size_plane = blk_ptr.PaletteSize[plane];
if (palette_size_plane > 0)
{
+ throw new NotImplementedException("Tokenizing palette not implemented.");
+ /*
Av1TransformSize tx_size =
blockGeometry.TransformSize[macroBlockModeInfo.Block.TransformDepth]; // inherit tx_size from 1st transform block;
svt_av1_tokenize_color_map(
@@ -503,12 +526,13 @@ private static void WriteModesBlock(
PALETTE_MAP,
0); // NO CDF update in entropy, the update will take place in arithmetic encode
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);
pack_map_tokens(ref writer, ref entropyCodingContext.tok, palette_size_plane, rowCount * columnCount);
// advance the pointer
entropyCodingContext.tok = tok;
+ */
}
}
}
@@ -546,16 +570,374 @@ private static void WriteModesBlock(
}
// 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
assert(blk_ptr.palette_info.color_idx_map != null && "free palette:Null");
EB_FREE(blk_ptr.palette_info.color_idx_map);
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);
+ }
+ }
+
+ ///
+ /// Get the contexts (left and top) for writing the intra luma mode for key frames.
+ /// Intended to be used for key frame only.
+ ///
+ /// SVT: svt_aom_get_kf_y_mode_ctx
+ 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];
+ }
+
+ ///
+ /// SVT: encode_intra_luma_mode_kf_av1
+ ///
+ 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.");
+ }
+
+ ///
+ /// SVT: svt_aom_filter_intra_allowed
+ ///
+ private static bool IsFilterIntraAllowed(
+ bool enableFilterIntra,
+ Av1BlockSize blockSize,
+ int paletteSize,
+ Av1PredictionMode mode)
+ => mode == Av1PredictionMode.DC && paletteSize == 0 && IsFilterIntraAllowedBlockSize(enableFilterIntra, blockSize);
+
+ ///
+ /// SVT: svt_aom_filter_intra_allowed_bsize
+ ///
+ private static bool IsFilterIntraAllowedBlockSize(bool enableFilterIntra, Av1BlockSize blockSize)
+ {
+ if (!enableFilterIntra)
+ {
+ return false;
+ }
+
+ return blockSize.GetWidth() <= 32 && blockSize.GetHeight() <= 32;
+ }
+
+ ///
+ /// SVT: write_intrabc_info
+ ///
+ 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);*/
+ }
+ }
+
+ ///
+ /// SVT: svt_aom_allow_intrabc
+ ///
+ private static bool IsIntraBlockCopyAllowed(ObuFrameHeader frameHeader)
+ => frameHeader.AllowScreenContentTools && frameHeader.AllowIntraBlockCopy;
+
+ ///
+ /// SVT: ec_update_neighbors
+ ///
+ private static void UpdateNeighbors(
+ Av1PictureControlSet pcs,
+ Av1EntropyCodingContext entropyCodingContext,
+ Point blockOrigin,
+ Av1EncoderBlockStruct blk_ptr,
+ ushort tile_idx,
+ Av1BlockSize blockSize)
+ {
+ Av1NeighborArrayUnit partition_context_na = pcs.PartitionContexts[tile_idx];
+ Av1NeighborArrayUnit luma_dc_sign_level_coeff_na = pcs.LuminanceDcSignLevelCoefficientNeighbors[tile_idx];
+ Av1NeighborArrayUnit cr_dc_sign_level_coeff_na = pcs.CrDcSignLevelCoefficientNeighbors[tile_idx];
+ Av1NeighborArrayUnit 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 partitionSpan = new(ref partition);
+ partition_context_na.UnitModeWrite(
+ partitionSpan,
+ blockOrigin,
+ size,
+ Av1NeighborArrayUnit.UnitMask.Left | Av1NeighborArrayUnit.UnitMask.Top);
+ if (skip_coeff)
+ {
+ byte dcSignLevelCoefficient = 0;
+ Span dcSignSpan = new(ref dcSignLevelCoefficient);
+
+ luma_dc_sign_level_coeff_na.UnitModeWrite(
+ dcSignSpan,
+ blockOrigin,
+ size,
+ Av1NeighborArrayUnit.UnitMask.Left | Av1NeighborArrayUnit.UnitMask.Top);
+
+ if (blk_geom.HasUv)
+ {
+ cb_dc_sign_level_coeff_na.UnitModeWrite(
+ dcSignSpan,
+ ((blockOrigin >> 3) << 3) >> 1,
+ size,
+ Av1NeighborArrayUnit.UnitMask.Left | Av1NeighborArrayUnit.UnitMask.Top);
+ cr_dc_sign_level_coeff_na.UnitModeWrite(
+ dcSignSpan,
+ ((blockOrigin >> 3) << 3) >> 1,
+ size,
+ Av1NeighborArrayUnit.UnitMask.Left | Av1NeighborArrayUnit.UnitMask.Top);
+ entropyCodingContext.CodedAreaSuperblockUv += blk_geom.BlockWidthUv * blk_geom.BlockHeightUv;
+ }
+
+ entropyCodingContext.CodedAreaSuperblock += blk_geom.BlockWidth * blk_geom.BlockHeight;
+ }
+ }
+
+ ///
+ /// SVT: svt_av1_allow_palette
+ ///
+ 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;
+ }
+
+ ///
+ /// SVT: svt_aom_allow_palette
+ ///
+ private static bool IsPaletteAllowed(bool allowScreenContentTools, Av1BlockSize blockSize)
+ => allowScreenContentTools &&
+ blockSize.GetWidth() <= 64 &&
+ blockSize.GetHeight() <= 64 &&
+ blockSize >= Av1BlockSize.Block8x8;
+
+ ///
+ /// SVT: write_cdef
+ ///
+ 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;
+ }
+ }
+
+ ///
+ /// SVT: set_mi_row_col
+ ///
+ 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;
+ }
+ }
}
///