mirror of https://github.com/SixLabors/ImageSharp
15 changed files with 1440 additions and 192 deletions
@ -0,0 +1,397 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Entropy; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline; |
|||
|
|||
/// <content>
|
|||
/// Provides live-probability final-block mode decisions for intra encoding.
|
|||
/// </content>
|
|||
internal static partial class Av1IntraSuperblockEncoder |
|||
{ |
|||
/// <summary>
|
|||
/// Builds the fixed 8x8 partition skeleton consumed by interleaved mode decision and tile writing.
|
|||
/// </summary>
|
|||
/// <param name="picture">The frame coding and mode-information state.</param>
|
|||
/// <param name="superblock">The reusable partition and final-block decisions.</param>
|
|||
/// <param name="superblockOrigin">The absolute luma-sample origin of the superblock.</param>
|
|||
public static void Prepare( |
|||
Av1PictureControlSet picture, |
|||
Av1Superblock superblock, |
|||
Point superblockOrigin) |
|||
{ |
|||
superblock.Workspace.Reset(); |
|||
int partitionIndex = 0; |
|||
PreparePartitionTree( |
|||
picture, |
|||
superblock, |
|||
superblockOrigin, |
|||
picture.Sequence.SequenceHeader.SuperblockSize, |
|||
ref partitionIndex); |
|||
} |
|||
|
|||
private static void PreparePartitionTree( |
|||
Av1PictureControlSet picture, |
|||
Av1Superblock superblock, |
|||
Point blockOrigin, |
|||
Av1BlockSize blockSize, |
|||
ref int partitionIndex) |
|||
{ |
|||
Av1EncoderCommon common = picture.Parent.Common; |
|||
Point modeInfoPosition = blockOrigin >> Av1Constants.ModeInfoSizeLog2; |
|||
if (modeInfoPosition.Y >= common.ModeInfoRowCount || modeInfoPosition.X >= common.ModeInfoColumnCount) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (blockSize == Av1BlockSize.Block8x8) |
|||
{ |
|||
superblock.CodingUnitPartitionTypes[partitionIndex++] = (byte)Av1PartitionType.None; |
|||
ref Av1MacroBlockModeInfo modeInfo = ref picture.GetMacroBlockModeInfo(modeInfoPosition); |
|||
modeInfo.Block = new Av1EncoderBlockModeInfo |
|||
{ |
|||
BlockSize = Av1BlockSize.Block8x8, |
|||
PartitionType = Av1PartitionType.None |
|||
}; |
|||
|
|||
return; |
|||
} |
|||
|
|||
superblock.CodingUnitPartitionTypes[partitionIndex++] = (byte)Av1PartitionType.Split; |
|||
Av1BlockSize subSize = Av1PartitionType.Split.GetBlockSubSize(blockSize); |
|||
int halfBlockSize = blockSize.GetWidth() >> 1; |
|||
|
|||
// The same preorder drives partition symbols, block decisions, and coefficient offsets.
|
|||
PreparePartitionTree(picture, superblock, blockOrigin, subSize, ref partitionIndex); |
|||
PreparePartitionTree(picture, superblock, blockOrigin + new Size(halfBlockSize, 0), subSize, ref partitionIndex); |
|||
PreparePartitionTree(picture, superblock, blockOrigin + new Size(0, halfBlockSize), subSize, ref partitionIndex); |
|||
PreparePartitionTree(picture, superblock, blockOrigin + new Size(halfBlockSize, halfBlockSize), subSize, ref partitionIndex); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Produces one final block at a time against the tile state immediately preceding its syntax.
|
|||
/// </summary>
|
|||
/// <typeparam name="TSample">The native unsigned sample storage type.</typeparam>
|
|||
/// <typeparam name="TOperator">The type-specific block encoding operations.</typeparam>
|
|||
internal struct ModeDecision<TSample, TOperator> : Av1TileWriter.IBlockEncodingHandler |
|||
where TSample : unmanaged |
|||
where TOperator : struct, IBlockEncodingOperator<TSample> |
|||
{ |
|||
private readonly Av1EncoderFrame<TSample>.PlanarView source; |
|||
private readonly Av1EncoderFrame<TSample>.PlanarView reconstruction; |
|||
private readonly Av1PictureControlSet picture; |
|||
private readonly Av1Superblock superblock; |
|||
private readonly Av1EncoderCoefficientBuffer coefficientBuffer; |
|||
private readonly Av1EncoderBlockWorkspace blockWorkspace; |
|||
private readonly ObuQuantizationParameters quantization; |
|||
private readonly Av1BitDepth bitDepth; |
|||
private readonly int rateMultiplier; |
|||
private int codedAreaLuma; |
|||
private int codedAreaChroma; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="ModeDecision{TSample, TOperator}"/> struct.
|
|||
/// </summary>
|
|||
/// <param name="source">The coded source frame.</param>
|
|||
/// <param name="reconstruction">The reconstructed frame updated by winning candidates.</param>
|
|||
/// <param name="picture">The frame coding and mode-information state.</param>
|
|||
/// <param name="superblock">The current superblock.</param>
|
|||
/// <param name="coefficientBuffer">The frame-owned quantized coefficient and transform state.</param>
|
|||
/// <param name="blockWorkspace">The reusable block arithmetic workspace.</param>
|
|||
public ModeDecision( |
|||
Av1EncoderFrame<TSample> source, |
|||
Av1EncoderFrame<TSample> reconstruction, |
|||
Av1PictureControlSet picture, |
|||
Av1Superblock superblock, |
|||
Av1EncoderCoefficientBuffer coefficientBuffer, |
|||
Av1EncoderBlockWorkspace blockWorkspace) |
|||
{ |
|||
this.source = source.CodedView; |
|||
this.reconstruction = reconstruction.CodedView; |
|||
this.picture = picture; |
|||
this.superblock = superblock; |
|||
this.coefficientBuffer = coefficientBuffer; |
|||
this.blockWorkspace = blockWorkspace; |
|||
this.quantization = picture.Parent.FrameHeader.QuantizationParameters; |
|||
this.bitDepth = picture.Sequence.SequenceHeader.ColorConfig.BitDepth; |
|||
this.rateMultiplier = Av1RateDistortion.GetKeyFrameRateMultiplier(this.quantization.QIndex[0], this.bitDepth); |
|||
this.codedAreaLuma = 0; |
|||
this.codedAreaChroma = 0; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public void EncodeBlock( |
|||
Av1SymbolEncoder writer, |
|||
Av1MacroBlockD macroBlock, |
|||
Point blockOrigin, |
|||
ushort tileIndex, |
|||
ref Av1MacroBlockModeInfo modeInfo, |
|||
ref Av1EncoderBlockStruct block) |
|||
{ |
|||
const Av1BlockSize BlockSize = Av1BlockSize.Block8x8; |
|||
const Av1TransformSize LumaTransformSize = Av1TransformSize.Size8x8; |
|||
int qIndex = this.quantization.QIndex[0]; |
|||
modeInfo.Block = new Av1EncoderBlockModeInfo |
|||
{ |
|||
BlockSize = BlockSize, |
|||
PartitionType = Av1PartitionType.None, |
|||
SegmentId = 0, |
|||
TransformSize = LumaTransformSize, |
|||
Mode = Av1PredictionMode.DC, |
|||
UvMode = Av1ChromaPredictionMode.DC |
|||
}; |
|||
|
|||
modeInfo.CdefStrength = 0; |
|||
block.HasChroma = !this.source.IsMonochrome; |
|||
block.QuantizationIndex = qIndex; |
|||
block.SegmentId = 0; |
|||
|
|||
Span<int> lumaCoefficients = this.coefficientBuffer.GetPlaneSpan(this.superblock.Index, Av1Plane.Y); |
|||
Span<Av1EncoderTransformBlockState> lumaTransformBlocks = |
|||
this.coefficientBuffer.GetTransformBlockSpan(this.superblock.Index, Av1Plane.Y); |
|||
|
|||
int lumaTransformIndex = this.codedAreaLuma / |
|||
Av1EncoderCoefficientBuffer.TransformBlockUnitCoefficientCount; |
|||
|
|||
ref Av1EncoderTransformBlockState lumaState = ref lumaTransformBlocks[lumaTransformIndex]; |
|||
modeInfo.Block.Mode = this.SelectLumaMode( |
|||
writer, |
|||
macroBlock, |
|||
blockOrigin, |
|||
tileIndex, |
|||
lumaCoefficients[this.codedAreaLuma..], |
|||
ref lumaState); |
|||
|
|||
this.codedAreaLuma += LumaTransformSize.GetSize2d(); |
|||
bool skipTransform = lumaState.EndOfBlock == 0; |
|||
if (this.source.IsMonochrome) |
|||
{ |
|||
modeInfo.Block.Skip = skipTransform; |
|||
return; |
|||
} |
|||
|
|||
ObuColorConfig colorConfig = this.picture.Sequence.SequenceHeader.ColorConfig; |
|||
int subsamplingX = colorConfig.SubSamplingX ? 1 : 0; |
|||
int subsamplingY = colorConfig.SubSamplingY ? 1 : 0; |
|||
Point chromaOrigin = new(blockOrigin.X >> subsamplingX, blockOrigin.Y >> subsamplingY); |
|||
Av1TransformSize chromaTransformSize = BlockSize.GetMaxUvTransformSize( |
|||
colorConfig.SubSamplingX, |
|||
colorConfig.SubSamplingY); |
|||
|
|||
Span<int> blueCoefficients = this.coefficientBuffer.GetPlaneSpan(this.superblock.Index, Av1Plane.U); |
|||
Span<int> redCoefficients = this.coefficientBuffer.GetPlaneSpan(this.superblock.Index, Av1Plane.V); |
|||
Span<Av1EncoderTransformBlockState> blueTransformBlocks = |
|||
this.coefficientBuffer.GetTransformBlockSpan(this.superblock.Index, Av1Plane.U); |
|||
Span<Av1EncoderTransformBlockState> redTransformBlocks = |
|||
this.coefficientBuffer.GetTransformBlockSpan(this.superblock.Index, Av1Plane.V); |
|||
|
|||
int chromaTransformIndex = this.codedAreaChroma / |
|||
Av1EncoderCoefficientBuffer.TransformBlockUnitCoefficientCount; |
|||
|
|||
ref Av1EncoderTransformBlockState blueState = ref blueTransformBlocks[chromaTransformIndex]; |
|||
ref Av1EncoderTransformBlockState redState = ref redTransformBlocks[chromaTransformIndex]; |
|||
EncodePlaneBlock<TSample, TOperator>( |
|||
this.source, |
|||
this.reconstruction, |
|||
this.blockWorkspace, |
|||
this.quantization, |
|||
this.bitDepth, |
|||
Av1Plane.U, |
|||
chromaOrigin, |
|||
chromaTransformSize, |
|||
blueCoefficients[this.codedAreaChroma..], |
|||
ref blueState); |
|||
|
|||
EncodePlaneBlock<TSample, TOperator>( |
|||
this.source, |
|||
this.reconstruction, |
|||
this.blockWorkspace, |
|||
this.quantization, |
|||
this.bitDepth, |
|||
Av1Plane.V, |
|||
chromaOrigin, |
|||
chromaTransformSize, |
|||
redCoefficients[this.codedAreaChroma..], |
|||
ref redState); |
|||
|
|||
// A block-level skip suppresses every coefficient symbol, so all coded planes must be empty.
|
|||
modeInfo.Block.Skip = skipTransform && blueState.EndOfBlock == 0 && redState.EndOfBlock == 0; |
|||
this.codedAreaChroma += chromaTransformSize.GetSize2d(); |
|||
} |
|||
|
|||
private Av1PredictionMode SelectLumaMode( |
|||
Av1SymbolEncoder writer, |
|||
Av1MacroBlockD macroBlock, |
|||
Point blockOrigin, |
|||
ushort tileIndex, |
|||
Span<int> retainedCoefficients, |
|||
ref Av1EncoderTransformBlockState retainedState) |
|||
{ |
|||
const Av1BlockSize BlockSize = Av1BlockSize.Block8x8; |
|||
const Av1TransformSize TransformSize = Av1TransformSize.Size8x8; |
|||
const int SampleCount = 8 * 8; |
|||
Buffer2DRegion<TSample> sourcePlane = this.source.GetPlane(Av1Plane.Y); |
|||
Buffer2DRegion<TSample> reconstructionPlane = this.reconstruction.GetPlane(Av1Plane.Y); |
|||
bool hasLeft = macroBlock.IsLeftAvailable; |
|||
bool hasAbove = macroBlock.IsUpAvailable; |
|||
ReadOnlySpan<TSample> above = hasAbove |
|||
? reconstructionPlane.DangerousGetRowSpan(blockOrigin.Y - 1).Slice(blockOrigin.X, 8) |
|||
: []; |
|||
|
|||
Span<TSample> left = stackalloc TSample[8]; |
|||
if (hasLeft) |
|||
{ |
|||
for (int row = 0; row < left.Length; row++) |
|||
{ |
|||
left[row] = reconstructionPlane.DangerousGetRowSpan(blockOrigin.Y + row)[blockOrigin.X - 1]; |
|||
} |
|||
} |
|||
|
|||
Av1TransformBlockContext blockContext = Av1TileWriter.GetTransformBlockContexts( |
|||
Av1ComponentType.Luminance, |
|||
this.picture.LuminanceDcSignLevelCoefficientNeighbors[tileIndex], |
|||
blockOrigin, |
|||
BlockSize, |
|||
TransformSize); |
|||
|
|||
Span<TSample> candidateReconstruction = stackalloc TSample[SampleCount]; |
|||
Span<int> candidateCoefficients = stackalloc int[SampleCount]; |
|||
Av1EncoderTransformBlockState candidateState = default; |
|||
long bestCost = this.GetLumaCandidateCost( |
|||
writer, |
|||
macroBlock, |
|||
sourcePlane, |
|||
blockOrigin, |
|||
above, |
|||
left, |
|||
hasLeft, |
|||
hasAbove, |
|||
Av1PredictionMode.DC, |
|||
blockContext, |
|||
candidateReconstruction, |
|||
candidateCoefficients, |
|||
ref candidateState); |
|||
|
|||
CopyCandidate( |
|||
candidateReconstruction, |
|||
candidateCoefficients, |
|||
reconstructionPlane, |
|||
blockOrigin, |
|||
retainedCoefficients, |
|||
candidateState, |
|||
ref retainedState); |
|||
|
|||
Av1PredictionMode bestMode = Av1PredictionMode.DC; |
|||
if (hasAbove) |
|||
{ |
|||
candidateState = default; |
|||
long verticalCost = this.GetLumaCandidateCost( |
|||
writer, |
|||
macroBlock, |
|||
sourcePlane, |
|||
blockOrigin, |
|||
above, |
|||
left, |
|||
hasLeft, |
|||
hasAbove, |
|||
Av1PredictionMode.Vertical, |
|||
blockContext, |
|||
candidateReconstruction, |
|||
candidateCoefficients, |
|||
ref candidateState); |
|||
|
|||
if (verticalCost < bestCost) |
|||
{ |
|||
CopyCandidate( |
|||
candidateReconstruction, |
|||
candidateCoefficients, |
|||
reconstructionPlane, |
|||
blockOrigin, |
|||
retainedCoefficients, |
|||
candidateState, |
|||
ref retainedState); |
|||
|
|||
bestMode = Av1PredictionMode.Vertical; |
|||
} |
|||
} |
|||
|
|||
return bestMode; |
|||
} |
|||
|
|||
private long GetLumaCandidateCost( |
|||
Av1SymbolEncoder writer, |
|||
Av1MacroBlockD macroBlock, |
|||
Buffer2DRegion<TSample> sourcePlane, |
|||
Point blockOrigin, |
|||
ReadOnlySpan<TSample> above, |
|||
ReadOnlySpan<TSample> left, |
|||
bool hasLeft, |
|||
bool hasAbove, |
|||
Av1PredictionMode mode, |
|||
Av1TransformBlockContext blockContext, |
|||
Span<TSample> candidateReconstruction, |
|||
Span<int> candidateCoefficients, |
|||
ref Av1EncoderTransformBlockState candidateState) |
|||
{ |
|||
const Av1BlockSize BlockSize = Av1BlockSize.Block8x8; |
|||
const Av1TransformSize TransformSize = Av1TransformSize.Size8x8; |
|||
long distortion = TOperator.EncodeCandidate( |
|||
this.blockWorkspace, |
|||
sourcePlane, |
|||
blockOrigin, |
|||
candidateReconstruction, |
|||
above, |
|||
left, |
|||
hasLeft, |
|||
hasAbove, |
|||
mode, |
|||
candidateCoefficients, |
|||
TransformSize, |
|||
this.quantization.QIndex[0], |
|||
this.quantization.DeltaQDc[(int)Av1Plane.Y], |
|||
this.quantization.DeltaQAc[(int)Av1Plane.Y], |
|||
this.bitDepth, |
|||
ref candidateState); |
|||
|
|||
int rate = Av1TileWriter.GetLumaModeCost(writer, macroBlock, BlockSize, mode); |
|||
rate += writer.GetCoefficientCost( |
|||
TransformSize, |
|||
Av1TransformType.DctDct, |
|||
mode, |
|||
candidateCoefficients, |
|||
Av1ComponentType.Luminance, |
|||
blockContext, |
|||
candidateState.EndOfBlock, |
|||
this.picture.Parent.FrameHeader.UseReducedTransformSet, |
|||
Av1FilterIntraMode.AllFilterIntraModes); |
|||
|
|||
return Av1RateDistortion.GetCost(this.rateMultiplier, rate, distortion); |
|||
} |
|||
|
|||
private static void CopyCandidate( |
|||
ReadOnlySpan<TSample> candidateReconstruction, |
|||
ReadOnlySpan<int> candidateCoefficients, |
|||
Buffer2DRegion<TSample> reconstructionPlane, |
|||
Point blockOrigin, |
|||
Span<int> retainedCoefficients, |
|||
Av1EncoderTransformBlockState candidateState, |
|||
ref Av1EncoderTransformBlockState retainedState) |
|||
{ |
|||
const int Width = 8; |
|||
candidateCoefficients.CopyTo(retainedCoefficients); |
|||
for (int row = 0; row < Width; row++) |
|||
{ |
|||
candidateReconstruction.Slice(row * Width, Width) |
|||
.CopyTo(reconstructionPlane.DangerousGetRowSpan(blockOrigin.Y + row).Slice(blockOrigin.X, Width)); |
|||
} |
|||
|
|||
retainedState = candidateState; |
|||
} |
|||
} |
|||
} |
|||
@ -1,81 +0,0 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline; |
|||
|
|||
/// <content>
|
|||
/// Defines the sample-storage operations used by single-tile intra encoding.
|
|||
/// </content>
|
|||
internal sealed partial class Av1IntraTileWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Defines type-specific superblock encoding without coupling tile traversal to sample storage width.
|
|||
/// </summary>
|
|||
/// <typeparam name="TSample">The native unsigned sample storage type.</typeparam>
|
|||
private interface ITileEncodingOperator<TSample> |
|||
where TSample : unmanaged |
|||
{ |
|||
/// <summary>
|
|||
/// Encodes and reconstructs one superblock.
|
|||
/// </summary>
|
|||
/// <param name="source">The coded source frame.</param>
|
|||
/// <param name="reconstruction">The reconstructed frame updated by the block transforms.</param>
|
|||
/// <param name="picture">The frame coding and mode-information state.</param>
|
|||
/// <param name="superblock">The reusable partition and final-block decisions.</param>
|
|||
/// <param name="coefficientBuffer">The frame-owned quantized coefficient and transform state.</param>
|
|||
/// <param name="blockWorkspace">The reusable block arithmetic workspace.</param>
|
|||
public static abstract void EncodeSuperblock( |
|||
Av1EncoderFrame<TSample> source, |
|||
Av1EncoderFrame<TSample> reconstruction, |
|||
Av1PictureControlSet picture, |
|||
Av1Superblock superblock, |
|||
Av1EncoderCoefficientBuffer coefficientBuffer, |
|||
Av1EncoderBlockWorkspace blockWorkspace); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Encodes tiles stored as eight-bit samples.
|
|||
/// </summary>
|
|||
private readonly struct ByteOperator : ITileEncodingOperator<byte> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void EncodeSuperblock( |
|||
Av1EncoderFrame<byte> source, |
|||
Av1EncoderFrame<byte> reconstruction, |
|||
Av1PictureControlSet picture, |
|||
Av1Superblock superblock, |
|||
Av1EncoderCoefficientBuffer coefficientBuffer, |
|||
Av1EncoderBlockWorkspace blockWorkspace) |
|||
=> Av1IntraSuperblockEncoder.Encode( |
|||
source, |
|||
reconstruction, |
|||
picture, |
|||
superblock, |
|||
coefficientBuffer, |
|||
blockWorkspace); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Encodes tiles stored as high-bit-depth samples.
|
|||
/// </summary>
|
|||
private readonly struct UInt16Operator : ITileEncodingOperator<ushort> |
|||
{ |
|||
/// <inheritdoc/>
|
|||
public static void EncodeSuperblock( |
|||
Av1EncoderFrame<ushort> source, |
|||
Av1EncoderFrame<ushort> reconstruction, |
|||
Av1PictureControlSet picture, |
|||
Av1Superblock superblock, |
|||
Av1EncoderCoefficientBuffer coefficientBuffer, |
|||
Av1EncoderBlockWorkspace blockWorkspace) |
|||
=> Av1IntraSuperblockEncoder.Encode( |
|||
source, |
|||
reconstruction, |
|||
picture, |
|||
superblock, |
|||
coefficientBuffer, |
|||
blockWorkspace); |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Entropy; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; |
|||
|
|||
/// <content>
|
|||
/// Defines the final-block decision contract used by interleaved tile encoding.
|
|||
/// </content>
|
|||
internal partial class Av1TileWriter |
|||
{ |
|||
/// <summary>
|
|||
/// Supplies a final block decision immediately before its symbols are written.
|
|||
/// </summary>
|
|||
internal interface IBlockEncodingHandler |
|||
{ |
|||
/// <summary>
|
|||
/// Encodes one final block against the current reconstructed neighbors and live tile probabilities.
|
|||
/// </summary>
|
|||
/// <param name="writer">The live tile symbol encoder.</param>
|
|||
/// <param name="macroBlock">The current block's mapped neighbor state.</param>
|
|||
/// <param name="blockOrigin">The absolute luma-sample origin.</param>
|
|||
/// <param name="tileIndex">The zero-based tile index.</param>
|
|||
/// <param name="modeInfo">The mode information to publish.</param>
|
|||
/// <param name="block">The encoder block state to publish.</param>
|
|||
void EncodeBlock( |
|||
Av1SymbolEncoder writer, |
|||
Av1MacroBlockD macroBlock, |
|||
Point blockOrigin, |
|||
ushort tileIndex, |
|||
ref Av1MacroBlockModeInfo modeInfo, |
|||
ref Av1EncoderBlockStruct block); |
|||
} |
|||
|
|||
private readonly struct PrecomputedBlockEncodingHandler : IBlockEncodingHandler |
|||
{ |
|||
public void EncodeBlock( |
|||
Av1SymbolEncoder writer, |
|||
Av1MacroBlockD macroBlock, |
|||
Point blockOrigin, |
|||
ushort tileIndex, |
|||
ref Av1MacroBlockModeInfo modeInfo, |
|||
ref Av1EncoderBlockStruct block) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue