mirror of https://github.com/SixLabors/ImageSharp
34 changed files with 3852 additions and 899 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,276 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
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; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Pipeline.LoopFilter; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Traverses reconstructed transform boundaries for in-place deblocking.
|
||||
|
/// </summary>
|
||||
|
internal static class Av1LoopFilterBase |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Supplies the selected block and transform state at a frame position.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TState">The owning encoder or decoder state.</typeparam>
|
||||
|
/// <typeparam name="TMode">The retained block-mode type.</typeparam>
|
||||
|
public interface IFrameOperator<TState, TMode> |
||||
|
where TMode : struct |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Resolves the block and filter parameters covering one plane position.
|
||||
|
/// </summary>
|
||||
|
/// <param name="state">The owning frame state.</param>
|
||||
|
/// <param name="position">The position in luma 4x4 units, adjusted for chroma ownership.</param>
|
||||
|
/// <param name="plane">The component plane.</param>
|
||||
|
/// <param name="pass">Zero for vertical boundaries; one for horizontal boundaries.</param>
|
||||
|
/// <param name="subX">The horizontal subsampling shift.</param>
|
||||
|
/// <param name="subY">The vertical subsampling shift.</param>
|
||||
|
/// <param name="blockIndex">The storage index identifying the owning prediction block.</param>
|
||||
|
/// <param name="skippedTransform">Whether an inter block omits its residual.</param>
|
||||
|
/// <param name="transformSize">The transform covering the plane position.</param>
|
||||
|
/// <param name="mode">The mode state retained for subsequent level derivation.</param>
|
||||
|
public static abstract void GetParameters( |
||||
|
TState state, |
||||
|
Point position, |
||||
|
Av1Plane plane, |
||||
|
int pass, |
||||
|
int subX, |
||||
|
int subY, |
||||
|
out int blockIndex, |
||||
|
out bool skippedTransform, |
||||
|
out Av1TransformSize transformSize, |
||||
|
out TMode mode); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Derives the level for a block whose boundary requires filtering.
|
||||
|
/// </summary>
|
||||
|
/// <param name="state">The owning frame state.</param>
|
||||
|
/// <param name="mode">The resolved block mode, read without another grid lookup or structure copy.</param>
|
||||
|
/// <param name="position">The block position in luma 4x4 units.</param>
|
||||
|
/// <param name="plane">The component plane.</param>
|
||||
|
/// <param name="pass">Zero for vertical boundaries; one for horizontal boundaries.</param>
|
||||
|
/// <returns>The adjusted level in the zero-to-63 domain.</returns>
|
||||
|
public static abstract int GetFilterLevel(TState state, ref TMode mode, Point position, Av1Plane plane, int pass); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Filters one plane band in vertical-then-horizontal boundary order.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TSample">The reconstructed sample storage type.</typeparam>
|
||||
|
/// <typeparam name="TState">The encoder or decoder state type.</typeparam>
|
||||
|
/// <typeparam name="TMode">The retained block-mode type.</typeparam>
|
||||
|
/// <typeparam name="TFrameOperator">The selected block-state accessor.</typeparam>
|
||||
|
/// <typeparam name="TVerticalOperator">The vertical sample accessor.</typeparam>
|
||||
|
/// <typeparam name="THorizontalOperator">The horizontal sample accessor.</typeparam>
|
||||
|
/// <param name="state">The owning frame state.</param>
|
||||
|
/// <param name="header">The decoded or selected frame header.</param>
|
||||
|
/// <param name="plane">The component plane.</param>
|
||||
|
/// <param name="rowStart">The inclusive band origin in luma 4x4 units.</param>
|
||||
|
/// <param name="rowEnd">The exclusive band limit in luma 4x4 units.</param>
|
||||
|
/// <param name="subX">The horizontal subsampling shift.</param>
|
||||
|
/// <param name="subY">The vertical subsampling shift.</param>
|
||||
|
/// <param name="samples">The plane storage including the required edge neighborhoods.</param>
|
||||
|
/// <param name="origin">The offset of the top-left coded sample within the storage.</param>
|
||||
|
/// <param name="stride">The plane stride in samples.</param>
|
||||
|
/// <param name="bitDepth">The component precision.</param>
|
||||
|
public static void FilterBand<TSample, TState, TMode, TFrameOperator, TVerticalOperator, THorizontalOperator>( |
||||
|
TState state, |
||||
|
ObuFrameHeader header, |
||||
|
Av1Plane plane, |
||||
|
int rowStart, |
||||
|
int rowEnd, |
||||
|
int subX, |
||||
|
int subY, |
||||
|
Span<TSample> samples, |
||||
|
int origin, |
||||
|
int stride, |
||||
|
int bitDepth) |
||||
|
where TSample : unmanaged |
||||
|
where TMode : struct |
||||
|
where TFrameOperator : struct, IFrameOperator<TState, TMode> |
||||
|
where TVerticalOperator : struct, Av1DeblockingFilter.IEdgeOperator<TSample> |
||||
|
where THorizontalOperator : struct, Av1DeblockingFilter.IEdgeOperator<TSample> |
||||
|
{ |
||||
|
int rowStep = 1 << subY; |
||||
|
int columnStep = 1 << subX; |
||||
|
|
||||
|
// Chroma remains in luma-grid coordinates. Each step still covers four samples in its own plane.
|
||||
|
// Finish vertical edges across each row before starting the horizontal traversal of this band.
|
||||
|
for (int row = rowStart; row < rowEnd; row += rowStep) |
||||
|
{ |
||||
|
for (int column = 0; column < header.ModeInfoColumnCount; column += columnStep) |
||||
|
{ |
||||
|
FilterEdge<TSample, TState, TMode, TFrameOperator, TVerticalOperator>( |
||||
|
state, header, plane, 0, row, column, subX, subY, samples, origin, stride, bitDepth); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Visit horizontal edges down each column: adjacent filters can read samples modified by earlier edges.
|
||||
|
for (int column = 0; column < header.ModeInfoColumnCount; column += columnStep) |
||||
|
{ |
||||
|
for (int row = rowStart; row < rowEnd; row += rowStep) |
||||
|
{ |
||||
|
FilterEdge<TSample, TState, TMode, TFrameOperator, THorizontalOperator>( |
||||
|
state, header, plane, 1, row, column, subX, subY, samples, origin, stride, bitDepth); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Combines the frame, superblock, segment, reference, and mode adjustments for one boundary.
|
||||
|
/// </summary>
|
||||
|
/// <param name="header">The frame's filter and segmentation state.</param>
|
||||
|
/// <param name="filterIndex">The luma-direction or chroma-component index.</param>
|
||||
|
/// <param name="baseLevel">The selected frame level for that component.</param>
|
||||
|
/// <param name="delta">The superblock adjustment for that component.</param>
|
||||
|
/// <param name="segmentId">The owning block's segment.</param>
|
||||
|
/// <param name="referenceFrame">The primary prediction reference.</param>
|
||||
|
/// <param name="mode">The luma prediction mode.</param>
|
||||
|
/// <returns>The adjusted level in the zero-to-63 domain.</returns>
|
||||
|
public static int GetFilterLevel( |
||||
|
ObuFrameHeader header, |
||||
|
int filterIndex, |
||||
|
int baseLevel, |
||||
|
int delta, |
||||
|
int segmentId, |
||||
|
Av1ReferenceFrameType referenceFrame, |
||||
|
Av1PredictionMode mode) |
||||
|
{ |
||||
|
ObuLoopFilterParameters parameters = header.LoopFilterParameters; |
||||
|
int level = Av1Math.Clip3(0, Av1Constants.MaxLoopFilter, baseLevel + delta); |
||||
|
ObuSegmentationLevelFeature feature = (ObuSegmentationLevelFeature)((int)ObuSegmentationLevelFeature.AlternativeLoopFilterYVertical + filterIndex); |
||||
|
ObuSegmentationParameters segmentation = header.SegmentationParameters; |
||||
|
|
||||
|
if (segmentation.IsFeatureActive(segmentId, feature)) |
||||
|
{ |
||||
|
level = Av1Math.Clip3( |
||||
|
0, |
||||
|
Av1Constants.MaxLoopFilter, |
||||
|
level + segmentation.GetFeatureData(segmentId, (int)feature)); |
||||
|
} |
||||
|
|
||||
|
if (parameters.ReferenceDeltaModeEnabled) |
||||
|
{ |
||||
|
int referenceScale = 1 << (level >> 5); |
||||
|
level += parameters.ReferenceDeltas[(int)referenceFrame] * referenceScale; |
||||
|
|
||||
|
if (referenceFrame > Av1ReferenceFrameType.Intra) |
||||
|
{ |
||||
|
// Every inter mode except the two global-motion modes belongs to the second mode-delta class.
|
||||
|
int modeDeltaIndex = mode is Av1PredictionMode.GlobalMotionVector or |
||||
|
Av1PredictionMode.GlobalGlobalMotionVector ? 0 : 1; |
||||
|
|
||||
|
level += parameters.ModeDeltas[modeDeltaIndex] * referenceScale; |
||||
|
} |
||||
|
|
||||
|
// Reference and mode adjustments use the same scale and may cancel beyond either limit.
|
||||
|
// Clipping the intermediate reference sum would discard part of that cancellation.
|
||||
|
level = Av1Math.Clip3(0, Av1Constants.MaxLoopFilter, level); |
||||
|
} |
||||
|
|
||||
|
return level; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Derives and applies the kernel at one transform boundary.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TSample">The reconstructed sample storage type.</typeparam>
|
||||
|
/// <typeparam name="TState">The encoder or decoder state type.</typeparam>
|
||||
|
/// <typeparam name="TMode">The retained block-mode type.</typeparam>
|
||||
|
/// <typeparam name="TFrameOperator">The selected block-state accessor.</typeparam>
|
||||
|
/// <typeparam name="TEdgeOperator">The oriented sample accessor.</typeparam>
|
||||
|
/// <param name="state">The owning frame state.</param>
|
||||
|
/// <param name="header">The decoded or selected frame header.</param>
|
||||
|
/// <param name="plane">The component plane.</param>
|
||||
|
/// <param name="pass">Zero for vertical boundaries; one for horizontal boundaries.</param>
|
||||
|
/// <param name="row">The boundary row in luma 4x4 units.</param>
|
||||
|
/// <param name="column">The boundary column in luma 4x4 units.</param>
|
||||
|
/// <param name="subX">The horizontal subsampling shift.</param>
|
||||
|
/// <param name="subY">The vertical subsampling shift.</param>
|
||||
|
/// <param name="samples">The plane storage including the required edge neighborhoods.</param>
|
||||
|
/// <param name="origin">The offset of the top-left coded sample.</param>
|
||||
|
/// <param name="stride">The plane stride in samples.</param>
|
||||
|
/// <param name="bitDepth">The component precision.</param>
|
||||
|
private static void FilterEdge<TSample, TState, TMode, TFrameOperator, TEdgeOperator>( |
||||
|
TState state, |
||||
|
ObuFrameHeader header, |
||||
|
Av1Plane plane, |
||||
|
int pass, |
||||
|
int row, |
||||
|
int column, |
||||
|
int subX, |
||||
|
int subY, |
||||
|
Span<TSample> samples, |
||||
|
int origin, |
||||
|
int stride, |
||||
|
int bitDepth) |
||||
|
where TSample : unmanaged |
||||
|
where TMode : struct |
||||
|
where TFrameOperator : struct, IFrameOperator<TState, TMode> |
||||
|
where TEdgeOperator : struct, Av1DeblockingFilter.IEdgeOperator<TSample> |
||||
|
{ |
||||
|
int x = column << Av1Constants.ModeInfoSizeLog2; |
||||
|
int y = row << Av1Constants.ModeInfoSizeLog2; |
||||
|
bool verticalBoundary = pass == 0; |
||||
|
if (x >= header.FrameSize.FrameWidth || y >= header.FrameSize.FrameHeight || (verticalBoundary ? x == 0 : y == 0)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// Subsampled chroma belongs to the bottom/right luma unit within its 8-sample footprint.
|
||||
|
Point position = new(column | subX, row | subY); |
||||
|
Point previous = new(position.X - (verticalBoundary ? 1 << subX : 0), position.Y - (verticalBoundary ? 0 : 1 << subY)); |
||||
|
TFrameOperator.GetParameters( |
||||
|
state, position, plane, pass, subX, subY, out int blockIndex, out bool skipped, out Av1TransformSize transform, out TMode mode); |
||||
|
|
||||
|
TFrameOperator.GetParameters( |
||||
|
state, |
||||
|
previous, |
||||
|
plane, |
||||
|
pass, |
||||
|
subX, |
||||
|
subY, |
||||
|
out int previousIndex, |
||||
|
out bool previousSkipped, |
||||
|
out Av1TransformSize previousTransform, |
||||
|
out TMode previousMode); |
||||
|
|
||||
|
int planeX = x >> subX; |
||||
|
int planeY = y >> subY; |
||||
|
bool isTransformEdge = verticalBoundary ? planeX % transform.GetWidth() == 0 : planeY % transform.GetHeight() == 0; |
||||
|
|
||||
|
// Residual-free intra predictions still need deblocking. Only skipped inter transforms suppress
|
||||
|
// internal boundaries; two different prediction blocks retain their common boundary.
|
||||
|
if (!isTransformEdge || (blockIndex == previousIndex && skipped && previousSkipped)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int level = TFrameOperator.GetFilterLevel(state, ref mode, position, plane, pass); |
||||
|
int filterLevel = level != 0 ? level : TFrameOperator.GetFilterLevel(state, ref previousMode, previous, plane, pass); |
||||
|
if (filterLevel == 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int size = verticalBoundary |
||||
|
? Math.Min(transform.GetWidth(), previousTransform.GetWidth()) |
||||
|
: Math.Min(transform.GetHeight(), previousTransform.GetHeight()); |
||||
|
|
||||
|
int kernelLength = plane == Av1Plane.Y ? size == 4 ? 4 : size == 8 ? 8 : 14 : size == 4 ? 4 : 6; |
||||
|
int sharpness = header.LoopFilterParameters.SharpnessLevel; |
||||
|
int shift = sharpness > 4 ? 2 : sharpness > 0 ? 1 : 0; |
||||
|
int limit = sharpness > 0 ? Av1Math.Clip3(1, 9 - sharpness, filterLevel >> shift) : Math.Max(1, filterLevel); |
||||
|
int boundaryLimit = (2 * (filterLevel + 2)) + limit; |
||||
|
|
||||
|
// Thresholds stay in the eight-bit domain; the sample operator and kernel handle storage and precision.
|
||||
|
// The explicit origin accommodates bordered encoder planes and the decoder's preceding-row view.
|
||||
|
Av1DeblockingFilter.Filter<TSample, TEdgeOperator>( |
||||
|
samples, origin + (planeY * stride) + planeX, stride, kernelLength, limit, boundaryLimit, filterLevel >> 4, bitDepth); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,141 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit; |
||||
|
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.LoopFilter; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Applies selected deblocking parameters to the encoder reconstruction.
|
||||
|
/// </summary>
|
||||
|
internal static class Av1LoopFilterEncoder |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Filters the completed reconstruction before it becomes a prediction reference.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TSample">The reconstructed sample storage type.</typeparam>
|
||||
|
/// <typeparam name="TVerticalOperator">The vertical sample accessor.</typeparam>
|
||||
|
/// <typeparam name="THorizontalOperator">The horizontal sample accessor.</typeparam>
|
||||
|
/// <param name="picture">The completed frame decisions.</param>
|
||||
|
/// <param name="reconstruction">The writable reconstructed component planes.</param>
|
||||
|
public static void ApplyFrame<TSample, TVerticalOperator, THorizontalOperator>( |
||||
|
Av1PictureControlSet picture, |
||||
|
Av1EncoderFrame<TSample> reconstruction) |
||||
|
where TSample : unmanaged |
||||
|
where TVerticalOperator : struct, Av1DeblockingFilter.IEdgeOperator<TSample> |
||||
|
where THorizontalOperator : struct, Av1DeblockingFilter.IEdgeOperator<TSample> |
||||
|
{ |
||||
|
ObuFrameHeader header = picture.Parent.FrameHeader; |
||||
|
ObuLoopFilterParameters parameters = header.LoopFilterParameters; |
||||
|
if (header.CodedLossless || header.AllowIntraBlockCopy || (parameters.FilterLevel[0] == 0 && parameters.FilterLevel[1] == 0)) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int planeCount = picture.Sequence.SequenceHeader.ColorConfig.PlaneCount; |
||||
|
for (int planeIndex = 0; planeIndex < planeCount; planeIndex++) |
||||
|
{ |
||||
|
ApplyPlane<TSample, TVerticalOperator, THorizontalOperator>(picture, reconstruction, (Av1Plane)planeIndex); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Filters one component using the current frame levels.
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TSample">The reconstructed sample storage type.</typeparam>
|
||||
|
/// <typeparam name="TVerticalOperator">The vertical sample accessor.</typeparam>
|
||||
|
/// <typeparam name="THorizontalOperator">The horizontal sample accessor.</typeparam>
|
||||
|
/// <param name="picture">The completed frame decisions.</param>
|
||||
|
/// <param name="reconstruction">The writable reconstructed component planes.</param>
|
||||
|
/// <param name="plane">The component to filter.</param>
|
||||
|
public static void ApplyPlane<TSample, TVerticalOperator, THorizontalOperator>( |
||||
|
Av1PictureControlSet picture, |
||||
|
Av1EncoderFrame<TSample> reconstruction, |
||||
|
Av1Plane plane) |
||||
|
where TSample : unmanaged |
||||
|
where TVerticalOperator : struct, Av1DeblockingFilter.IEdgeOperator<TSample> |
||||
|
where THorizontalOperator : struct, Av1DeblockingFilter.IEdgeOperator<TSample> |
||||
|
{ |
||||
|
ObuFrameHeader header = picture.Parent.FrameHeader; |
||||
|
ObuLoopFilterParameters parameters = header.LoopFilterParameters; |
||||
|
int level = plane switch |
||||
|
{ |
||||
|
Av1Plane.U => parameters.FilterLevelU, |
||||
|
Av1Plane.V => parameters.FilterLevelV, |
||||
|
_ => Math.Max(parameters.FilterLevel[0], parameters.FilterLevel[1]) |
||||
|
}; |
||||
|
|
||||
|
if (level == 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
Buffer2DRegion<TSample> samples = reconstruction.CodedView.GetPlane(plane); |
||||
|
int origin = (samples.Bounds.Y * samples.Stride) + samples.Bounds.X; |
||||
|
int subX = plane == Av1Plane.Y ? 0 : reconstruction.ChromaSubsamplingX; |
||||
|
int subY = plane == Av1Plane.Y ? 0 : reconstruction.ChromaSubsamplingY; |
||||
|
int rowsPerBand = 1 << (Av1Constants.MaxSuperBlockSizeLog2 - Av1Constants.ModeInfoSizeLog2); |
||||
|
|
||||
|
// The frame owner supplies one contiguous allocation. Retain its full bordered view so kernels may
|
||||
|
// access their edge neighborhoods without copying the plane or materializing decoder frame state.
|
||||
|
Span<TSample> storage = samples.Buffer.DangerousGetSingleSpan(); |
||||
|
for (int rowStart = 0; rowStart < header.ModeInfoRowCount; rowStart += rowsPerBand) |
||||
|
{ |
||||
|
int rowEnd = Math.Min(rowStart + rowsPerBand, header.ModeInfoRowCount); |
||||
|
Av1LoopFilterBase.FilterBand<TSample, Av1PictureControlSet, Av1EncoderBlockModeInfo, FrameOperator, TVerticalOperator, THorizontalOperator>( |
||||
|
picture, header, plane, rowStart, rowEnd, subX, subY, storage, origin, samples.Stride, reconstruction.LumaBitDepth); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Reads deblocking parameters from the retained encoder mode grid.
|
||||
|
/// </summary>
|
||||
|
private readonly struct FrameOperator : Av1LoopFilterBase.IFrameOperator<Av1PictureControlSet, Av1EncoderBlockModeInfo> |
||||
|
{ |
||||
|
/// <inheritdoc/>
|
||||
|
public static void GetParameters( |
||||
|
Av1PictureControlSet state, |
||||
|
Point position, |
||||
|
Av1Plane plane, |
||||
|
int pass, |
||||
|
int subX, |
||||
|
int subY, |
||||
|
out int blockIndex, |
||||
|
out bool skippedTransform, |
||||
|
out Av1TransformSize transformSize, |
||||
|
out Av1EncoderBlockModeInfo mode) |
||||
|
{ |
||||
|
blockIndex = state.ModeInfoGrid.Span[(position.Y * state.ModeInfoStride) + position.X]; |
||||
|
mode = state.ModeInfoAllocation.Span[blockIndex].Block; |
||||
|
skippedTransform = mode.Skip && mode.ReferenceFrame > Av1ReferenceFrameType.Intra; |
||||
|
|
||||
|
// The selected encoder transform size is uniform within each luma block. Chroma owns its
|
||||
|
// maximum plane transform independently of luma splits, while lossless segments always use 4x4.
|
||||
|
transformSize = state.Parent.FrameHeader.LosslessArray[mode.SegmentId] |
||||
|
? Av1TransformSize.Size4x4 |
||||
|
: plane == Av1Plane.Y ? mode.TransformSize : mode.BlockSize.GetMaxUvTransformSize(subX != 0, subY != 0); |
||||
|
} |
||||
|
|
||||
|
/// <inheritdoc/>
|
||||
|
public static int GetFilterLevel(Av1PictureControlSet state, ref Av1EncoderBlockModeInfo mode, Point position, Av1Plane plane, int pass) |
||||
|
{ |
||||
|
ObuFrameHeader header = state.Parent.FrameHeader; |
||||
|
ObuLoopFilterParameters parameters = header.LoopFilterParameters; |
||||
|
int index = plane == Av1Plane.Y ? pass : (int)plane + 1; |
||||
|
int level = index switch |
||||
|
{ |
||||
|
0 => parameters.FilterLevel[0], |
||||
|
1 => parameters.FilterLevel[1], |
||||
|
2 => parameters.FilterLevelU, |
||||
|
_ => parameters.FilterLevelV |
||||
|
}; |
||||
|
|
||||
|
// The encoder does not emit superblock filter deltas. Frame, segment, reference, and mode
|
||||
|
// adjustments still follow the same clipping and scaling as the decoder.
|
||||
|
return Av1LoopFilterBase.GetFilterLevel(header, index, level, 0, mode.SegmentId, mode.ReferenceFrame, mode.Mode); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
Loading…
Reference in new issue