Browse Source

Implement live AV1 8x8 partition search

pull/2633/head
James Jackson-South 4 weeks ago
parent
commit
02253984e7
  1. 10
      HEIF_IMPLEMENTATION_PLAN.md
  2. 49
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolEncoder.cs
  3. 17
      src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1EncoderBlockWorkspace.cs
  4. 6
      src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs
  5. 45
      src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ChromaModeDecision.cs
  6. 22
      src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.IntraBlockCopyModeDecision.cs
  7. 843
      src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ModeDecision.cs
  8. 6
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1EncoderPictureBuffer.cs
  9. 29
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.BlockEncoding.cs
  10. 255
      src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.cs
  11. 3
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs
  12. 63
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs
  13. 3
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderModeInfoBufferTests.cs
  14. 15
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1IntraBlockCopyTests.cs
  15. 74
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1IntraSuperblockEncoderTests.cs

10
HEIF_IMPLEMENTATION_PLAN.md

File diff suppressed because one or more lines are too long

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

@ -722,6 +722,15 @@ internal class Av1SymbolEncoder : IDisposable
public int GetDisplacementVectorSearchCost(Av1MotionVector value, Av1MotionVector reference) public int GetDisplacementVectorSearchCost(Av1MotionVector value, Av1MotionVector reference)
=> this.displacementVector.GetCost(this.writer, value, reference); => this.displacementVector.GetCost(this.writer, value, reference);
/// <summary>
/// Gets the current fixed-point cost of a complete block partition symbol.
/// </summary>
/// <param name="partitionType">The partition type to measure.</param>
/// <param name="context">The partition probability context.</param>
/// <returns>The rate cost in 1/512-bit units.</returns>
public int GetPartitionTypeCost(Av1PartitionType partitionType, int context)
=> Av1ProbabilityCost.GetSymbolCost(this.tilePartitionTypes[context], (int)partitionType);
/// <summary> /// <summary>
/// Writes a complete block partition type using the selected partition context. /// Writes a complete block partition type using the selected partition context.
/// </summary> /// </summary>
@ -747,6 +756,26 @@ internal class Av1SymbolEncoder : IDisposable
w.WriteBoolean(value, frequency); w.WriteBoolean(value, frequency);
} }
/// <summary>
/// Gets the current fixed-point cost of the split-versus-horizontal boundary decision.
/// </summary>
/// <param name="partitionType">The split or horizontal partition outcome.</param>
/// <param name="blockSize">The current block size.</param>
/// <param name="context">The partition probability context.</param>
/// <returns>The rate cost in 1/512-bit units.</returns>
public int GetSplitOrHorizontalCost(Av1PartitionType partitionType, Av1BlockSize blockSize, int context)
{
int frequency = (int)Av1SymbolDecoder.GetSplitOrHorizontalFrequency(
this.tilePartitionTypes,
blockSize,
context);
return Av1ProbabilityCost.GetProbabilityCost(
partitionType == Av1PartitionType.Split
? frequency
: Av1Distribution.ProbabilityTop - frequency);
}
/// <summary> /// <summary>
/// Writes the split-versus-vertical boundary decision for a block clipped at the right tile edge. /// Writes the split-versus-vertical boundary decision for a block clipped at the right tile edge.
/// </summary> /// </summary>
@ -761,6 +790,26 @@ internal class Av1SymbolEncoder : IDisposable
w.WriteBoolean(value, frequency); w.WriteBoolean(value, frequency);
} }
/// <summary>
/// Gets the current fixed-point cost of the split-versus-vertical boundary decision.
/// </summary>
/// <param name="partitionType">The split or vertical partition outcome.</param>
/// <param name="blockSize">The current block size.</param>
/// <param name="context">The partition probability context.</param>
/// <returns>The rate cost in 1/512-bit units.</returns>
public int GetSplitOrVerticalCost(Av1PartitionType partitionType, Av1BlockSize blockSize, int context)
{
int frequency = (int)Av1SymbolDecoder.GetSplitOrVerticalFrequency(
this.tilePartitionTypes,
blockSize,
context);
return Av1ProbabilityCost.GetProbabilityCost(
partitionType == Av1PartitionType.Split
? frequency
: Av1Distribution.ProbabilityTop - frequency);
}
/// <summary> /// <summary>
/// Encodes one transform block's coefficient syntax using scan-order probability contexts. /// Encodes one transform block's coefficient syntax using scan-order probability contexts.
/// </summary> /// </summary>

17
src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1EncoderBlockWorkspace.cs

@ -31,7 +31,8 @@ internal sealed class Av1EncoderBlockWorkspace : IDisposable
MaximumCoefficientCount + MaximumCoefficientCount +
MaximumCoefficientCount + MaximumCoefficientCount +
Av1TransformWorkspace.MaximumLength + Av1TransformWorkspace.MaximumLength +
IntraBlockCopyStorageLength; IntraBlockCopyStorageLength +
PartitionContextStorageLength;
private const int ResidualStorageLength = MaximumResidualCount / 2; private const int ResidualStorageLength = MaximumResidualCount / 2;
private const int TransformCoefficientOffset = ResidualStorageLength; private const int TransformCoefficientOffset = ResidualStorageLength;
@ -64,6 +65,11 @@ internal sealed class Av1EncoderBlockWorkspace : IDisposable
IntraBlockCopyResidualStorageLength + IntraBlockCopyResidualStorageLength +
IntraBlockCopyCoefficientStorageLength; IntraBlockCopyCoefficientStorageLength;
private const int PartitionContextStorageOffset =
IntraBlockCopySampleStorageOffset + IntraBlockCopyStorageLength;
private const int PartitionContextStorageLength = 4;
/// <summary> /// <summary>
/// Owns the complete reusable block workspace in 32-bit elements so every transform region is naturally aligned. /// Owns the complete reusable block workspace in 32-bit elements so every transform region is naturally aligned.
/// </summary> /// </summary>
@ -100,6 +106,15 @@ internal sealed class Av1EncoderBlockWorkspace : IDisposable
public Span<int> TransformWorkspace public Span<int> TransformWorkspace
=> this.owner.Memory.Span.Slice(TransformWorkspaceOffset, Av1TransformWorkspace.MaximumLength); => this.owner.Memory.Span.Slice(TransformWorkspaceOffset, Av1TransformWorkspace.MaximumLength);
/// <summary>
/// Gets storage for the coefficient and transform edges restored after an 8x8 partition trial.
/// </summary>
public Span<byte> PartitionContexts
=> MemoryMarshal.AsBytes(
this.owner.Memory.Span.Slice(
PartitionContextStorageOffset,
PartitionContextStorageLength));
/// <summary> /// <summary>
/// Gets the reusable storage used while comparing spatial, chroma-from-luma, filter-intra, and palette candidates. /// Gets the reusable storage used while comparing spatial, chroma-from-luma, filter-intra, and palette candidates.
/// </summary> /// </summary>

6
src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1FrameEncoder.cs

@ -300,7 +300,8 @@ internal static class Av1FrameEncoder
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
image.Width, image.Width,
image.Height); image.Height,
disallow4x4AllFrames: effort < 9);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
configuration, configuration,
@ -363,7 +364,8 @@ internal static class Av1FrameEncoder
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
image.Width, image.Width,
image.Height); image.Height,
disallow4x4AllFrames: effort < 9);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
configuration, configuration,

45
src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ChromaModeDecision.cs

@ -46,6 +46,7 @@ internal static partial class Av1IntraSuperblockEncoder
Av1MacroBlockModeInfo modeInfo, Av1MacroBlockModeInfo modeInfo,
Point lumaOrigin, Point lumaOrigin,
Point chromaOrigin, Point chromaOrigin,
Av1BlockSize blockSize,
ushort tileIndex, ushort tileIndex,
Av1PredictionMode lumaMode, Av1PredictionMode lumaMode,
Av1TransformSize transformSize, Av1TransformSize transformSize,
@ -59,7 +60,6 @@ internal static partial class Av1IntraSuperblockEncoder
out sbyte selectedChromaFromLumaSigns, out sbyte selectedChromaFromLumaSigns,
out long selectedCost) out long selectedCost)
{ {
const Av1BlockSize BlockSize = Av1BlockSize.Block8x8;
Av1EncoderModeDecisionWorkspace<TSample> workspace = Av1EncoderModeDecisionWorkspace<TSample> workspace =
this.blockWorkspace.GetModeDecisionWorkspace<TSample>(); this.blockWorkspace.GetModeDecisionWorkspace<TSample>();
@ -73,11 +73,24 @@ internal static partial class Av1IntraSuperblockEncoder
int modeInfoColumn = lumaOrigin.X >> Av1Constants.ModeInfoSizeLog2; int modeInfoColumn = lumaOrigin.X >> Av1Constants.ModeInfoSizeLog2;
bool hasLeft = macroBlock.IsLeftAvailable; bool hasLeft = macroBlock.IsLeftAvailable;
bool hasAbove = macroBlock.IsUpAvailable; bool hasAbove = macroBlock.IsUpAvailable;
// Subsampled chroma belongs to the bottom-right luma unit in its shared 8x8 region. Its external
// references therefore begin before that region, not immediately beside the owning 4x4 luma block.
if (subsamplingX != 0 && blockSize.Get4x4WideCount() < Av1BlockSize.Block8x8.Get4x4WideCount())
{
hasLeft = modeInfoColumn - 1 > macroBlock.Tile.ModeInfoColumnStart;
}
if (subsamplingY != 0 && blockSize.Get4x4HighCount() < Av1BlockSize.Block8x8.Get4x4HighCount())
{
hasAbove = modeInfoRow - 1 > macroBlock.Tile.ModeInfoRowStart;
}
bool rightAvailable = modeInfoColumn + (transformSize.Get4x4WideCount() << subsamplingX) < macroBlock.Tile.ModeInfoColumnEnd; bool rightAvailable = modeInfoColumn + (transformSize.Get4x4WideCount() << subsamplingX) < macroBlock.Tile.ModeInfoColumnEnd;
bool bottomAvailable = modeInfoRow + (transformSize.Get4x4HighCount() << subsamplingY) < macroBlock.Tile.ModeInfoRowEnd; bool bottomAvailable = modeInfoRow + (transformSize.Get4x4HighCount() << subsamplingY) < macroBlock.Tile.ModeInfoRowEnd;
bool hasTopRight = Av1IntraReferenceAvailability.HasTopRight( bool hasTopRight = Av1IntraReferenceAvailability.HasTopRight(
this.picture.Sequence.SequenceHeader.SuperblockSize, this.picture.Sequence.SequenceHeader.SuperblockSize,
BlockSize, blockSize,
modeInfoRow, modeInfoRow,
modeInfoColumn, modeInfoColumn,
hasAbove, hasAbove,
@ -91,7 +104,7 @@ internal static partial class Av1IntraSuperblockEncoder
bool hasBottomLeft = Av1IntraReferenceAvailability.HasBottomLeft( bool hasBottomLeft = Av1IntraReferenceAvailability.HasBottomLeft(
this.picture.Sequence.SequenceHeader.SuperblockSize, this.picture.Sequence.SequenceHeader.SuperblockSize,
BlockSize, blockSize,
modeInfoRow, modeInfoRow,
modeInfoColumn, modeInfoColumn,
bottomAvailable, bottomAvailable,
@ -139,7 +152,7 @@ internal static partial class Av1IntraSuperblockEncoder
ReadOnlySpan<TSample> blueLeft = blueLeftStorage.Slice(1, height * 2); ReadOnlySpan<TSample> blueLeft = blueLeftStorage.Slice(1, height * 2);
ReadOnlySpan<TSample> redAbove = redAboveStorage.Slice(1, width * 2); ReadOnlySpan<TSample> redAbove = redAboveStorage.Slice(1, width * 2);
ReadOnlySpan<TSample> redLeft = redLeftStorage.Slice(1, height * 2); ReadOnlySpan<TSample> redLeft = redLeftStorage.Slice(1, height * 2);
Av1BlockSize chromaBlockSize = BlockSize.GetSubsampled(colorConfig.SubSamplingX, colorConfig.SubSamplingY); Av1BlockSize chromaBlockSize = blockSize.GetSubsampled(colorConfig.SubSamplingX, colorConfig.SubSamplingY);
Av1TransformBlockContext blueContext = Av1TileWriter.GetTransformBlockContexts( Av1TransformBlockContext blueContext = Av1TileWriter.GetTransformBlockContexts(
Av1ComponentType.Chroma, Av1ComponentType.Chroma,
this.picture.CbDcSignLevelCoefficientNeighbors[tileIndex], this.picture.CbDcSignLevelCoefficientNeighbors[tileIndex],
@ -172,11 +185,13 @@ internal static partial class Av1IntraSuperblockEncoder
{ {
0 => 1, 0 => 1,
1 => baseModeCount, 1 => baseModeCount,
_ => baseModeCount + (directionalModeCount * deltaCount) _ when blockSize >= Av1BlockSize.Block8x8 => baseModeCount + (directionalModeCount * deltaCount),
_ => baseModeCount
}; };
bool hasLumaPalette = paletteInfo.PaletteSizes[0] != 0; bool hasLumaPalette = paletteInfo.PaletteSizes[0] != 0;
int paletteDisabledCost = this.picture.Parent.FrameHeader.AllowScreenContentTools int paletteDisabledCost = blockSize >= Av1BlockSize.Block8x8 &&
this.picture.Parent.FrameHeader.AllowScreenContentTools
? writer.GetPaletteUvModeCost(false, hasLumaPalette) ? writer.GetPaletteUvModeCost(false, hasLumaPalette)
: 0; : 0;
@ -209,6 +224,7 @@ internal static partial class Av1IntraSuperblockEncoder
lumaMode, lumaMode,
chromaMode, chromaMode,
angleDelta, angleDelta,
blockSize,
chromaOrigin, chromaOrigin,
transformSize, transformSize,
blueSource, blueSource,
@ -257,7 +273,7 @@ internal static partial class Av1IntraSuperblockEncoder
} }
} }
bool chromaFromLumaAllowed = BlockSize.AllowsChromaFromLuma( bool chromaFromLumaAllowed = blockSize.AllowsChromaFromLuma(
this.picture.Parent.FrameHeader.LosslessArray[modeInfo.Block.SegmentId], this.picture.Parent.FrameHeader.LosslessArray[modeInfo.Block.SegmentId],
colorConfig.SubSamplingX, colorConfig.SubSamplingX,
colorConfig.SubSamplingY); colorConfig.SubSamplingY);
@ -265,9 +281,15 @@ internal static partial class Av1IntraSuperblockEncoder
if (this.effort >= 4 && chromaFromLumaAllowed) if (this.effort >= 4 && chromaFromLumaAllowed)
{ {
Span<short> lumaQ3 = workspace.ChromaFromLumaSamples; Span<short> lumaQ3 = workspace.ChromaFromLumaSamples;
Point chromaLumaOrigin = new(
chromaOrigin.X << subsamplingX,
chromaOrigin.Y << subsamplingY);
// CfL consumes the complete luma region represented by this chroma block, which starts before
// the bottom-right ownership point for subsampled 4x4 luma leaves.
TOperator.PrepareChromaFromLuma( TOperator.PrepareChromaFromLuma(
this.reconstruction.GetPlane(Av1Plane.Y), this.reconstruction.GetPlane(Av1Plane.Y),
lumaOrigin, chromaLumaOrigin,
lumaQ3, lumaQ3,
transformSize, transformSize,
colorConfig.SubSamplingX, colorConfig.SubSamplingX,
@ -345,7 +367,7 @@ internal static partial class Av1IntraSuperblockEncoder
this.picture.Parent.FrameHeader, this.picture.Parent.FrameHeader,
colorConfig, colorConfig,
modeInfo, modeInfo,
BlockSize, blockSize,
lumaMode, lumaMode,
Av1ChromaPredictionMode.ChromaFromLuma, Av1ChromaPredictionMode.ChromaFromLuma,
0); 0);
@ -457,6 +479,7 @@ internal static partial class Av1IntraSuperblockEncoder
} }
if (this.effort >= 5 && if (this.effort >= 5 &&
blockSize == Av1BlockSize.Block8x8 &&
this.picture.Parent.FrameHeader.AllowScreenContentTools && this.picture.Parent.FrameHeader.AllowScreenContentTools &&
this.SelectChromaPalette( this.SelectChromaPalette(
writer, writer,
@ -544,6 +567,7 @@ internal static partial class Av1IntraSuperblockEncoder
Av1PredictionMode lumaMode, Av1PredictionMode lumaMode,
Av1ChromaPredictionMode chromaMode, Av1ChromaPredictionMode chromaMode,
int angleDelta, int angleDelta,
Av1BlockSize blockSize,
Point chromaOrigin, Point chromaOrigin,
Av1TransformSize transformSize, Av1TransformSize transformSize,
Buffer2DRegion<TSample> blueSource, Buffer2DRegion<TSample> blueSource,
@ -564,7 +588,6 @@ internal static partial class Av1IntraSuperblockEncoder
ref Av1EncoderTransformBlockState candidateBlueState, ref Av1EncoderTransformBlockState candidateBlueState,
ref Av1EncoderTransformBlockState candidateRedState) ref Av1EncoderTransformBlockState candidateRedState)
{ {
const Av1BlockSize BlockSize = Av1BlockSize.Block8x8;
Av1PredictionMode predictionMode = chromaMode.ToLumaMode(); Av1PredictionMode predictionMode = chromaMode.ToLumaMode();
// Intra chroma derives one transform type from the shared UV prediction mode. The type is not // Intra chroma derives one transform type from the shared UV prediction mode. The type is not
@ -623,7 +646,7 @@ internal static partial class Av1IntraSuperblockEncoder
this.picture.Parent.FrameHeader, this.picture.Parent.FrameHeader,
this.picture.Sequence.SequenceHeader.ColorConfig, this.picture.Sequence.SequenceHeader.ColorConfig,
modeInfo, modeInfo,
BlockSize, blockSize,
lumaMode, lumaMode,
chromaMode, chromaMode,
angleDelta); angleDelta);

22
src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.IntraBlockCopyModeDecision.cs

@ -20,13 +20,12 @@ internal static partial class Av1IntraSuperblockEncoder
where TSample : unmanaged where TSample : unmanaged
where TOperator : struct, IBlockEncodingOperator<TSample> where TOperator : struct, IBlockEncodingOperator<TSample>
{ {
private void SelectIntraBlockCopy( private long SelectIntraBlockCopy(
Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1MacroBlockD macroBlock, Av1MacroBlockD macroBlock,
Point blockOrigin, Point blockOrigin,
ushort tileIndex, ushort tileIndex,
long regularModeCost, long regularCost,
int regularEmptyTransformRate,
ref Av1MacroBlockModeInfo modeInfo, ref Av1MacroBlockModeInfo modeInfo,
ref Av1EncoderBlockStruct block, ref Av1EncoderBlockStruct block,
ref Av1EncoderPaletteInfo paletteInfo) ref Av1EncoderPaletteInfo paletteInfo)
@ -99,21 +98,11 @@ internal static partial class Av1IntraSuperblockEncoder
if (uniqueCandidateCount == 0) if (uniqueCandidateCount == 0)
{ {
return; return regularCost;
} }
// Mode-decision costs already contain the selected coefficient syntax but not the block's skip
// or IBC choice. When regular intra skips, replace its empty-coefficient rate with skip syntax.
int skipContext = Av1TileWriter.GetSkipContext(macroBlock); int skipContext = Av1TileWriter.GetSkipContext(macroBlock);
int regularRateAdjustment = writer.GetUseIntraBlockCopyCost(false) + long bestCost = regularCost;
writer.GetSkipCost(modeInfo.Block.Skip, skipContext);
if (modeInfo.Block.Skip)
{
regularRateAdjustment -= regularEmptyTransformRate;
}
long bestCost = regularModeCost + Av1RateDistortion.GetCost(this.rateMultiplier, regularRateAdjustment, 0);
bool hasSelectedCandidate = false; bool hasSelectedCandidate = false;
bool selectedSkip = false; bool selectedSkip = false;
Av1MotionVector selectedVector = default; Av1MotionVector selectedVector = default;
@ -369,7 +358,7 @@ internal static partial class Av1IntraSuperblockEncoder
if (!hasSelectedCandidate) if (!hasSelectedCandidate)
{ {
return; return bestCost;
} }
// Only the winning vector is now visible to later coding blocks. This single publication keeps // Only the winning vector is now visible to later coding blocks. This single publication keeps
@ -440,6 +429,7 @@ internal static partial class Av1IntraSuperblockEncoder
block.PredictionUnit.ChromaFromLumaSigns = 0; block.PredictionUnit.ChromaFromLumaSigns = 0;
paletteInfo = default; paletteInfo = default;
this.picture.SetDisplacementVector(modeInfoPosition, selectedVector); this.picture.SetDisplacementVector(modeInfoPosition, selectedVector);
return bestCost;
} }
private void EvaluateIntraBlockCopyPlane( private void EvaluateIntraBlockCopyPlane(

843
src/ImageSharp/Formats/Heif/Av1/Pipeline/Av1IntraSuperblockEncoder.ModeDecision.cs

File diff suppressed because it is too large

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

@ -32,19 +32,21 @@ internal sealed class Av1EncoderPictureBuffer : IDisposable
/// <param name="frameHeader">The frame header defining dimensions and tiles.</param> /// <param name="frameHeader">The frame header defining dimensions and tiles.</param>
/// <param name="width">The visible luma width.</param> /// <param name="width">The visible luma width.</param>
/// <param name="height">The visible luma height.</param> /// <param name="height">The visible luma height.</param>
/// <param name="disallow4x4AllFrames">Whether each allocated mode-information value represents an 8x8 region.</param>
public Av1EncoderPictureBuffer( public Av1EncoderPictureBuffer(
Configuration configuration, Configuration configuration,
ObuSequenceHeader sequenceHeader, ObuSequenceHeader sequenceHeader,
ObuFrameHeader frameHeader, ObuFrameHeader frameHeader,
int width, int width,
int height) int height,
bool disallow4x4AllFrames)
{ {
const int ContextAlignmentLog2 = Av1Constants.MaxSuperBlockSizeLog2 - Av1Constants.ModeInfoSizeLog2; const int ContextAlignmentLog2 = Av1Constants.MaxSuperBlockSizeLog2 - Av1Constants.ModeInfoSizeLog2;
this.modeInfo = new Av1EncoderModeInfoBuffer( this.modeInfo = new Av1EncoderModeInfoBuffer(
configuration, configuration,
width, width,
height, height,
disallow4x4AllFrames: true); disallow4x4AllFrames);
int alignedModeInfoRowCount = Av1Math.AlignPowerOf2(this.modeInfo.ModeInfoRowCount, ContextAlignmentLog2); int alignedModeInfoRowCount = Av1Math.AlignPowerOf2(this.modeInfo.ModeInfoRowCount, ContextAlignmentLog2);
int lumaLeftLength = alignedModeInfoRowCount; int lumaLeftLength = alignedModeInfoRowCount;

29
src/ImageSharp/Formats/Heif/Av1/Tiling/Av1TileWriter.BlockEncoding.cs

@ -15,6 +15,24 @@ internal partial class Av1TileWriter
/// </summary> /// </summary>
internal interface IBlockEncodingHandler internal interface IBlockEncodingHandler
{ {
/// <summary>
/// Selects the partition used for the current tree node.
/// </summary>
/// <param name="writer">The live tile symbol encoder.</param>
/// <param name="macroBlock">The tile-local macroblock state.</param>
/// <param name="blockOrigin">The absolute luma-sample origin.</param>
/// <param name="tileIndex">The zero-based tile index.</param>
/// <param name="blockSize">The current square partition size.</param>
/// <param name="preparedPartition">The partition retained before live analysis.</param>
/// <returns>The partition to encode.</returns>
Av1PartitionType SelectPartition(
Av1SymbolEncoder writer,
Av1MacroBlockD macroBlock,
Point blockOrigin,
ushort tileIndex,
Av1BlockSize blockSize,
Av1PartitionType preparedPartition);
/// <summary> /// <summary>
/// Encodes one final block against the current reconstructed neighbors and live tile probabilities. /// Encodes one final block against the current reconstructed neighbors and live tile probabilities.
/// </summary> /// </summary>
@ -37,6 +55,17 @@ internal partial class Av1TileWriter
private readonly struct PrecomputedBlockEncodingHandler : IBlockEncodingHandler private readonly struct PrecomputedBlockEncodingHandler : IBlockEncodingHandler
{ {
/// <inheritdoc/>
public Av1PartitionType SelectPartition(
Av1SymbolEncoder writer,
Av1MacroBlockD macroBlock,
Point blockOrigin,
ushort tileIndex,
Av1BlockSize blockSize,
Av1PartitionType preparedPartition)
=> preparedPartition;
/// <inheritdoc/>
public void EncodeBlock( public void EncodeBlock(
Av1SymbolEncoder writer, Av1SymbolEncoder writer,
Av1MacroBlockD macroBlock, Av1MacroBlockD macroBlock,

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

@ -149,7 +149,19 @@ internal partial class Av1TileWriter
return; return;
} }
Av1PartitionType partition = (Av1PartitionType)superblock.CodingUnitPartitionTypes[partitionIndex++]; int currentPartitionIndex = partitionIndex++;
Av1PartitionType preparedPartition =
(Av1PartitionType)superblock.CodingUnitPartitionTypes[currentPartitionIndex];
Av1PartitionType partition = blockEncoder.SelectPartition(
writer,
entropyCodingContext.MacroBlock,
blockOrigin,
tileIndex,
blockSize,
preparedPartition);
superblock.CodingUnitPartitionTypes[currentPartitionIndex] = (byte)partition;
Av1BlockSize subSize = partition.GetBlockSubSize(blockSize); Av1BlockSize subSize = partition.GetBlockSubSize(blockSize);
int halfBlockSize = blockSize.GetWidth() >> 1; int halfBlockSize = blockSize.GetWidth() >> 1;
int quarterBlockSize = blockSize.GetWidth() >> 2; int quarterBlockSize = blockSize.GetWidth() >> 2;
@ -232,54 +244,89 @@ internal partial class Av1TileWriter
break; break;
case Av1PartitionType.Split: case Av1PartitionType.Split:
WritePartitionTree( if (blockSize == Av1BlockSize.Block8x8)
pcs, {
entropyCodingContext, // A split 8x8 node terminates in four 4x4 coding blocks. AV1 does not carry another
writer, // partition symbol at that size, so the children are final blocks rather than tree nodes.
superblock, for (int childIndex = 0; childIndex < 4; childIndex++)
coefficientBuffer, {
tileIndex, Point childOrigin = blockOrigin + new Size(
subSize, (childIndex & 1) * halfBlockSize,
blockOrigin, (childIndex >> 1) * halfBlockSize);
ref partitionIndex,
ref finalBlockIndex, Point childModeInfoPosition = childOrigin >> Av1Constants.ModeInfoSizeLog2;
ref blockEncoder); if (childModeInfoPosition.Y >= common.ModeInfoRowCount ||
WritePartitionTree( childModeInfoPosition.X >= common.ModeInfoColumnCount)
pcs, {
entropyCodingContext, continue;
writer, }
superblock,
coefficientBuffer, WriteFinalBlock(
tileIndex, pcs,
subSize, entropyCodingContext,
blockOrigin + new Size(halfBlockSize, 0), writer,
ref partitionIndex, superblock,
ref finalBlockIndex, coefficientBuffer,
ref blockEncoder); tileIndex,
WritePartitionTree( childOrigin,
pcs, ref finalBlockIndex,
entropyCodingContext, ref blockEncoder);
writer, }
superblock, }
coefficientBuffer, else
tileIndex, {
subSize, WritePartitionTree(
blockOrigin + new Size(0, halfBlockSize), pcs,
ref partitionIndex, entropyCodingContext,
ref finalBlockIndex, writer,
ref blockEncoder); superblock,
WritePartitionTree( coefficientBuffer,
pcs, tileIndex,
entropyCodingContext, subSize,
writer, blockOrigin,
superblock, ref partitionIndex,
coefficientBuffer, ref finalBlockIndex,
tileIndex, ref blockEncoder);
subSize,
blockOrigin + new Size(halfBlockSize, halfBlockSize), WritePartitionTree(
ref partitionIndex, pcs,
ref finalBlockIndex, entropyCodingContext,
ref blockEncoder); writer,
superblock,
coefficientBuffer,
tileIndex,
subSize,
blockOrigin + new Size(halfBlockSize, 0),
ref partitionIndex,
ref finalBlockIndex,
ref blockEncoder);
WritePartitionTree(
pcs,
entropyCodingContext,
writer,
superblock,
coefficientBuffer,
tileIndex,
subSize,
blockOrigin + new Size(0, halfBlockSize),
ref partitionIndex,
ref finalBlockIndex,
ref blockEncoder);
WritePartitionTree(
pcs,
entropyCodingContext,
writer,
superblock,
coefficientBuffer,
tileIndex,
subSize,
blockOrigin + new Size(halfBlockSize, halfBlockSize),
ref partitionIndex,
ref finalBlockIndex,
ref blockEncoder);
}
break; break;
case Av1PartitionType.HorizontalA: case Av1PartitionType.HorizontalA:
@ -592,6 +639,47 @@ internal partial class Av1TileWriter
edgeMask); edgeMask);
} }
/// <summary>
/// Gets the partition-symbol rate from the above and left contexts available at a block origin.
/// </summary>
/// <param name="pcs">The picture coding state.</param>
/// <param name="writer">The live tile symbol encoder.</param>
/// <param name="blockSize">The square parent block size.</param>
/// <param name="partitionType">The partition type to measure.</param>
/// <param name="blockOrigin">The block origin in samples.</param>
/// <param name="partitionContexts">The partition neighbor arrays for the tile.</param>
/// <returns>The rate cost in 1/512-bit units.</returns>
public static int GetPartitionCost(
Av1PictureControlSet pcs,
Av1SymbolEncoder writer,
Av1BlockSize blockSize,
Av1PartitionType partitionType,
Point blockOrigin,
Av1NeighborArrayUnit<Av1PartitionContext> partitionContexts)
{
int context = GetPartitionContext(
pcs,
blockSize,
blockOrigin,
partitionContexts,
out bool hasRows,
out bool hasColumns);
if (!hasRows && !hasColumns)
{
return 0;
}
if (hasRows && hasColumns)
{
return writer.GetPartitionTypeCost(partitionType, context);
}
return !hasRows
? writer.GetSplitOrHorizontalCost(partitionType, blockSize, context)
: writer.GetSplitOrVerticalCost(partitionType, blockSize, context);
}
/// <summary> /// <summary>
/// Writes a partition symbol using the above and left partition contexts available at a block origin. /// Writes a partition symbol using the above and left partition contexts available at a block origin.
/// </summary> /// </summary>
@ -616,33 +704,13 @@ internal partial class Av1TileWriter
return; return;
} }
int halfBlockModeInfoCount = blockSize.Get4x4WideCount() >> 1; int context_index = GetPartitionContext(
Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; pcs,
bool has_rows = modeInfoPosition.Y + halfBlockModeInfoCount < pcs.Parent.Common.ModeInfoRowCount; blockSize,
bool has_cols = modeInfoPosition.X + halfBlockModeInfoCount < pcs.Parent.Common.ModeInfoColumnCount; blockOrigin,
partition_context_na,
int partition_context_left_neighbor_index = partition_context_na.GetLeftIndex(blockOrigin); out bool has_rows,
int partition_context_top_neighbor_index = partition_context_na.GetTopIndex(blockOrigin); out bool has_cols);
int context_index = 0;
byte above_ctx =
(byte)(partition_context_na.Top[partition_context_top_neighbor_index].Above == byte.MaxValue
? 0
: partition_context_na.Top[partition_context_top_neighbor_index].Above);
byte left_ctx =
(byte)(partition_context_na.Left[partition_context_left_neighbor_index].Left == byte.MaxValue
? 0
: partition_context_na.Left[partition_context_left_neighbor_index].Left);
int blockSizeLog2 = blockSize.Get4x4WidthLog2() - 1;
int above = (above_ctx >> blockSizeLog2) & 1, left = (left_ctx >> blockSizeLog2) & 1;
Guard.IsTrue(blockSize.Get4x4WidthLog2() == blockSize.Get4x4HeightLog2(), nameof(blockSize), "Blocks need to be square.");
Guard.IsTrue(blockSizeLog2 >= 0, nameof(blockSizeLog2), "bsl needs to be a positive integer.");
// Each square block-size level owns four contexts selected by the current split bit of its neighbors.
context_index = ((left * 2) + above) + (blockSizeLog2 * Av1Constants.PartitionProbabilitySet);
if (!has_rows && !has_cols) if (!has_rows && !has_cols)
{ {
@ -666,6 +734,39 @@ internal partial class Av1TileWriter
return; return;
} }
private static int GetPartitionContext(
Av1PictureControlSet pcs,
Av1BlockSize blockSize,
Point blockOrigin,
Av1NeighborArrayUnit<Av1PartitionContext> partitionContexts,
out bool hasRows,
out bool hasColumns)
{
int halfBlockModeInfoCount = blockSize.Get4x4WideCount() >> 1;
Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2;
hasRows = modeInfoPosition.Y + halfBlockModeInfoCount < pcs.Parent.Common.ModeInfoRowCount;
hasColumns = modeInfoPosition.X + halfBlockModeInfoCount < pcs.Parent.Common.ModeInfoColumnCount;
int leftIndex = partitionContexts.GetLeftIndex(blockOrigin);
int topIndex = partitionContexts.GetTopIndex(blockOrigin);
byte aboveContext = partitionContexts.Top[topIndex].Above == byte.MaxValue
? (byte)0
: partitionContexts.Top[topIndex].Above;
byte leftContext = partitionContexts.Left[leftIndex].Left == byte.MaxValue
? (byte)0
: partitionContexts.Left[leftIndex].Left;
int blockSizeLog2 = blockSize.Get4x4WidthLog2() - 1;
int above = (aboveContext >> blockSizeLog2) & 1;
int left = (leftContext >> blockSizeLog2) & 1;
Guard.IsTrue(blockSize.Get4x4WidthLog2() == blockSize.Get4x4HeightLog2(), nameof(blockSize), "Blocks need to be square.");
Guard.IsTrue(blockSizeLog2 >= 0, nameof(blockSizeLog2), "bsl needs to be a positive integer.");
// Each square block-size level owns four contexts selected by the current split bit of its neighbors.
return ((left * 2) + above) + (blockSizeLog2 * Av1Constants.PartitionProbabilitySet);
}
/// <summary> /// <summary>
/// Writes the segmentation, prediction, transform, coefficient, and filter syntax for one final coding block. /// Writes the segmentation, prediction, transform, coefficient, and filter syntax for one final coding block.
/// </summary> /// </summary>
@ -1900,7 +2001,7 @@ internal partial class Av1TileWriter
/// <param name="subsamplingX">The horizontal chroma subsampling shift.</param> /// <param name="subsamplingX">The horizontal chroma subsampling shift.</param>
/// <param name="subsamplingY">The vertical chroma subsampling shift.</param> /// <param name="subsamplingY">The vertical chroma subsampling shift.</param>
/// <returns>The aligned origin in chroma samples.</returns> /// <returns>The aligned origin in chroma samples.</returns>
private static Point GetChromaBlockOrigin(Point lumaOrigin, int subsamplingX, int subsamplingY) public static Point GetChromaBlockOrigin(Point lumaOrigin, int subsamplingX, int subsamplingY)
=> new( => new(
(lumaOrigin.X >> (Av1Constants.ModeInfoSizeLog2 + subsamplingX)) << Av1Constants.ModeInfoSizeLog2, (lumaOrigin.X >> (Av1Constants.ModeInfoSizeLog2 + subsamplingX)) << Av1Constants.ModeInfoSizeLog2,
(lumaOrigin.Y >> (Av1Constants.ModeInfoSizeLog2 + subsamplingY)) << Av1Constants.ModeInfoSizeLog2); (lumaOrigin.Y >> (Av1Constants.ModeInfoSizeLog2 + subsamplingY)) << Av1Constants.ModeInfoSizeLog2);

3
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1CoefficientsEntropyTests.cs

@ -368,7 +368,8 @@ public class Av1CoefficientsEntropyTests
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
Av1PictureControlSet picture = pictureBuffer.Picture; Av1PictureControlSet picture = pictureBuffer.Picture;
Av1NeighborArrayUnit<Av1EncoderPaletteInfo> paletteContexts = Assert.Single(picture.PaletteContexts); Av1NeighborArrayUnit<Av1EncoderPaletteInfo> paletteContexts = Assert.Single(picture.PaletteContexts);

63
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderFrameTests.cs

@ -153,6 +153,69 @@ public class Av1EncoderFrameTests
} }
} }
[Fact]
public void EncodeEffortNineSelectsSubEightPartition()
{
const int Size = 16;
using Image<Rgba32> source = new(Size, Size);
for (int y = 0; y < Size; y++)
{
Span<Rgba32> row = source.Frames.RootFrame.PixelBuffer.DangerousGetRowSpan(y);
for (int x = 0; x < Size; x++)
{
// The bottom-right 8x8 uses horizontal prediction on its left half and vertical prediction
// on its right half. Twelve source values keep a parent palette from reproducing both halves.
byte value;
if (x < 8 && y < 8)
{
value = 128;
}
else if (y < 8)
{
value = (byte)(16 + ((x - 8) * 20));
}
else
{
value = x < 12
? (byte)(176 + ((y - 8) * 9))
: (byte)(16 + ((x - 8) * 20));
}
row[x] = new Rgba32(value, value, value);
}
}
using MemoryStream stream = new();
_ = Av1FrameEncoder.Encode(
Configuration.Default,
source.Frames.RootFrame,
stream,
CreateColorConfig(Av1BitDepth.EightBit, Av1ColorFormat.Yuv400),
qIndex: 4,
effort: 9);
byte[] payload = stream.ToArray();
using Av1Decoder decoder = new(Configuration.Default);
using Image<Rgba32> decoded = decoder.Decode<Rgba32>(payload);
Av1FrameInfo frameInfo = Assert.IsType<Av1FrameInfo>(decoder.FrameInfo);
Point[] leafPositions =
[
new(2, 2),
new(3, 2),
new(2, 3),
new(3, 3)
];
foreach (Point leafPosition in leafPositions)
{
Assert.Equal(
Av1BlockSize.Block4x8,
frameInfo.GetModeInfoAt(leafPosition).BlockSize);
}
Assert.Equal(new Size(Size, Size), decoded.Size);
}
[Theory] [Theory]
[InlineData(EightBit)] [InlineData(EightBit)]
[InlineData(TenBit)] [InlineData(TenBit)]

3
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1EncoderModeInfoBufferTests.cs

@ -99,7 +99,8 @@ public class Av1EncoderModeInfoBufferTests
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
Width, Width,
Height)) Height,
disallow4x4AllFrames: true))
{ {
allocations = allocator.AllocationLog.ToArray(); allocations = allocator.AllocationLog.ToArray();
Assert.Equal(2, allocations.Length); Assert.Equal(2, allocations.Length);

15
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1IntraBlockCopyTests.cs

@ -100,7 +100,8 @@ public class Av1IntraBlockCopyTests
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
Av1PictureControlSet picture = buffer.Picture; Av1PictureControlSet picture = buffer.Picture;
Point candidatePosition = new(80, 12); Point candidatePosition = new(80, 12);
@ -161,7 +162,8 @@ public class Av1IntraBlockCopyTests
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
Av1PictureControlSet picture = buffer.Picture; Av1PictureControlSet picture = buffer.Picture;
Point modeInfoPosition = new(80, 0); Point modeInfoPosition = new(80, 0);
@ -254,7 +256,8 @@ public class Av1IntraBlockCopyTests
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderFrameBuffer<byte> source = new( using Av1EncoderFrameBuffer<byte> source = new(
Configuration.Default, Configuration.Default,
@ -339,7 +342,8 @@ public class Av1IntraBlockCopyTests
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderFrameBuffer<byte> source = new( using Av1EncoderFrameBuffer<byte> source = new(
Configuration.Default, Configuration.Default,
@ -434,7 +438,8 @@ public class Av1IntraBlockCopyTests
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderFrameBuffer<byte> source = new( using Av1EncoderFrameBuffer<byte> source = new(
Configuration.Default, Configuration.Default,

74
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1IntraSuperblockEncoderTests.cs

@ -244,7 +244,8 @@ public class Av1IntraSuperblockEncoderTests
picture.Sequence.SequenceHeader, picture.Sequence.SequenceHeader,
picture.Parent.FrameHeader, picture.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer tileCoefficients = new( using Av1EncoderCoefficientBuffer tileCoefficients = new(
Configuration.Default, Configuration.Default,
@ -320,7 +321,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
Av1PictureControlSet picture = pictureBuffer.Picture; Av1PictureControlSet picture = pictureBuffer.Picture;
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
@ -393,7 +395,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer liveCoefficients = new( using Av1EncoderCoefficientBuffer liveCoefficients = new(
Configuration.Default, Configuration.Default,
@ -517,7 +520,8 @@ public class Av1IntraSuperblockEncoderTests
picture.Sequence.SequenceHeader, picture.Sequence.SequenceHeader,
picture.Parent.FrameHeader, picture.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer tileCoefficients = new( using Av1EncoderCoefficientBuffer tileCoefficients = new(
Configuration.Default, Configuration.Default,
@ -572,7 +576,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -664,7 +669,8 @@ public class Av1IntraSuperblockEncoderTests
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -921,7 +927,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -1133,7 +1140,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -1246,7 +1254,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -1406,7 +1415,8 @@ public class Av1IntraSuperblockEncoderTests
pilotTemplate.Sequence.SequenceHeader, pilotTemplate.Sequence.SequenceHeader,
pilotTemplate.Parent.FrameHeader, pilotTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer pilotCoefficients = new( using Av1EncoderCoefficientBuffer pilotCoefficients = new(
Configuration.Default, Configuration.Default,
@ -1507,7 +1517,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -1747,7 +1758,8 @@ public class Av1IntraSuperblockEncoderTests
pilotTemplate.Sequence.SequenceHeader, pilotTemplate.Sequence.SequenceHeader,
pilotTemplate.Parent.FrameHeader, pilotTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer pilotCoefficients = new( using Av1EncoderCoefficientBuffer pilotCoefficients = new(
Configuration.Default, Configuration.Default,
@ -1903,7 +1915,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -2101,7 +2114,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -2243,7 +2257,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -2370,7 +2385,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -2480,7 +2496,8 @@ public class Av1IntraSuperblockEncoderTests
sequenceHeader, sequenceHeader,
frameHeader, frameHeader,
Width, Width,
Height); Height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -2738,7 +2755,8 @@ public class Av1IntraSuperblockEncoderTests
pictureTemplate.Sequence.SequenceHeader, pictureTemplate.Sequence.SequenceHeader,
pictureTemplate.Parent.FrameHeader, pictureTemplate.Parent.FrameHeader,
width, width,
height); height,
disallow4x4AllFrames: true);
using Av1EncoderCoefficientBuffer coefficients = new( using Av1EncoderCoefficientBuffer coefficients = new(
Configuration.Default, Configuration.Default,
@ -3035,6 +3053,16 @@ public class Av1IntraSuperblockEncoderTests
/// </summary> /// </summary>
public int Count { get; private set; } public int Count { get; private set; }
/// <inheritdoc/>
public readonly Av1PartitionType SelectPartition(
Av1SymbolEncoder writer,
Av1MacroBlockD macroBlock,
Point blockOrigin,
ushort tileIndex,
Av1BlockSize blockSize,
Av1PartitionType preparedPartition)
=> preparedPartition;
/// <inheritdoc/> /// <inheritdoc/>
public void EncodeBlock( public void EncodeBlock(
Av1SymbolEncoder writer, Av1SymbolEncoder writer,
@ -3100,6 +3128,16 @@ public class Av1IntraSuperblockEncoderTests
/// </summary> /// </summary>
public int Count { get; private set; } public int Count { get; private set; }
/// <inheritdoc/>
public readonly Av1PartitionType SelectPartition(
Av1SymbolEncoder writer,
Av1MacroBlockD macroBlock,
Point blockOrigin,
ushort tileIndex,
Av1BlockSize blockSize,
Av1PartitionType preparedPartition)
=> preparedPartition;
/// <inheritdoc/> /// <inheritdoc/>
public void EncodeBlock( public void EncodeBlock(
Av1SymbolEncoder writer, Av1SymbolEncoder writer,

Loading…
Cancel
Save