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; + } + } } ///