mirror of https://github.com/SixLabors/ImageSharp
5 changed files with 568 additions and 2 deletions
@ -0,0 +1,174 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Hevc; |
||||
|
|
||||
|
/// <content>
|
||||
|
/// Provides reference-sample preparation and filter selection.
|
||||
|
/// </content>
|
||||
|
internal static partial class HevcIntraPredictor |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the angular-distance threshold for reference filtering at each supported block size.
|
||||
|
/// </summary>
|
||||
|
private static ReadOnlySpan<byte> ReferenceFilterThresholds => [10, 7, 1, 0]; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the temporary sample count required while preparing prediction references.
|
||||
|
/// </summary>
|
||||
|
/// <param name="log2Size">The base-two logarithm of the square prediction-block side.</param>
|
||||
|
/// <param name="unitWidth">The horizontal availability-unit width in plane samples.</param>
|
||||
|
/// <returns>The required number of <see cref="ushort"/> elements.</returns>
|
||||
|
public static int GetReferenceScratchLength(int log2Size, int unitWidth) |
||||
|
{ |
||||
|
DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); |
||||
|
return (4 << log2Size) + unitWidth; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Determines whether the selected mode uses filtered prediction references.
|
||||
|
/// </summary>
|
||||
|
/// <param name="plane">The reconstructed plane.</param>
|
||||
|
/// <param name="mode">The effective prediction mode in the inclusive range zero through thirty-four.</param>
|
||||
|
/// <param name="log2Size">The base-two logarithm of the square prediction-block side.</param>
|
||||
|
/// <param name="chromaFormat">The HEVC chroma-format identifier.</param>
|
||||
|
/// <param name="intraSmoothingDisabled">Whether the sequence disables intra-reference smoothing.</param>
|
||||
|
/// <returns><see langword="true"/> when the prepared references require filtering; otherwise, <see langword="false"/>.</returns>
|
||||
|
public static bool ShouldFilterReferenceSamples(HevcPlane plane, int mode, int log2Size, byte chromaFormat, bool intraSmoothingDisabled) |
||||
|
{ |
||||
|
DebugGuard.MustBeBetweenOrEqualTo(mode, PlanarMode, 34, nameof(mode)); |
||||
|
DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); |
||||
|
if (intraSmoothingDisabled || (plane != HevcPlane.Y && chromaFormat != 3) || mode == DcMode) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
int angularDistance = Math.Min(Math.Abs(mode - HorizontalMode), Math.Abs(mode - VerticalMode)); |
||||
|
return angularDistance > ReferenceFilterThresholds[log2Size - 2]; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Prepares substituted top and left references from one reconstructed picture plane.
|
||||
|
/// </summary>
|
||||
|
/// <param name="picture">The reconstructed still-picture planes.</param>
|
||||
|
/// <param name="plane">The plane containing the prediction block.</param>
|
||||
|
/// <param name="x">The prediction-block left coordinate in plane samples.</param>
|
||||
|
/// <param name="y">The prediction-block top coordinate in plane samples.</param>
|
||||
|
/// <param name="log2Size">The base-two logarithm of the square prediction-block side.</param>
|
||||
|
/// <param name="unitWidth">The horizontal availability-unit width in plane samples.</param>
|
||||
|
/// <param name="unitHeight">The vertical availability-unit height in plane samples.</param>
|
||||
|
/// <param name="availableUnits">
|
||||
|
/// The availability flags ordered from the bottom-most below-left unit upward through top-left, then from the
|
||||
|
/// left-most above unit through the right-most above-right unit.
|
||||
|
/// </param>
|
||||
|
/// <param name="top">The destination top-left, top, and top-right reference samples.</param>
|
||||
|
/// <param name="left">The destination top-left, left, and below-left reference samples.</param>
|
||||
|
/// <param name="scratch">The caller-owned temporary storage sized by <see cref="GetReferenceScratchLength(int, int)"/>.</param>
|
||||
|
public static void PrepareReferenceSamples( |
||||
|
HevcPictureBuffer picture, |
||||
|
HevcPlane plane, |
||||
|
int x, |
||||
|
int y, |
||||
|
int log2Size, |
||||
|
int unitWidth, |
||||
|
int unitHeight, |
||||
|
ReadOnlySpan<bool> availableUnits, |
||||
|
Span<ushort> top, |
||||
|
Span<ushort> left, |
||||
|
Span<ushort> scratch) |
||||
|
{ |
||||
|
DebugGuard.MustBeBetweenOrEqualTo(log2Size, 2, 5, nameof(log2Size)); |
||||
|
int size = 1 << log2Size; |
||||
|
int referenceLength = (size * 2) + 1; |
||||
|
int leftUnitCount = (size * 2) / unitHeight; |
||||
|
int aboveUnitCount = (size * 2) / unitWidth; |
||||
|
int totalUnitCount = leftUnitCount + aboveUnitCount + 1; |
||||
|
ReadOnlySpan<bool> availability = availableUnits[..totalUnitCount]; |
||||
|
|
||||
|
if (availability.IndexOf(true) < 0) |
||||
|
{ |
||||
|
ushort midpoint = (ushort)(1 << (picture.GetBitDepth(plane) - 1)); |
||||
|
top[..referenceLength].Fill(midpoint); |
||||
|
left[..referenceLength].Fill(midpoint); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (availability.IndexOf(false) < 0) |
||||
|
{ |
||||
|
picture.GetRowSpan(plane, y - 1).Slice(x - 1, referenceLength).CopyTo(top); |
||||
|
left[0] = top[0]; |
||||
|
for (int i = 1; i < referenceLength; i++) |
||||
|
{ |
||||
|
left[i] = picture.GetRowSpan(plane, y + i - 1)[x - 1]; |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
int leftSampleCount = size * 2; |
||||
|
int lineLength = leftSampleCount + unitWidth + (size * 2); |
||||
|
Span<ushort> line = scratch[..lineLength]; |
||||
|
line.Fill((ushort)(1 << (picture.GetBitDepth(plane) - 1))); |
||||
|
|
||||
|
// The logical line runs from the bottom-most below-left sample towards the corner and then to the farthest
|
||||
|
// above-right sample. This makes substitution a forward fill across availability units.
|
||||
|
for (int unit = 0; unit < leftUnitCount; unit++) |
||||
|
{ |
||||
|
if (!availability[unit]) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
int sourceY = y + ((leftUnitCount - unit - 1) * unitHeight); |
||||
|
int destinationEnd = ((unit + 1) * unitHeight) - 1; |
||||
|
for (int offset = 0; offset < unitHeight; offset++) |
||||
|
{ |
||||
|
line[destinationEnd - offset] = picture.GetRowSpan(plane, sourceY + offset)[x - 1]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int cornerUnit = leftUnitCount; |
||||
|
if (availability[cornerUnit]) |
||||
|
{ |
||||
|
line.Slice(leftSampleCount, unitWidth).Fill(picture.GetRowSpan(plane, y - 1)[x - 1]); |
||||
|
} |
||||
|
|
||||
|
ReadOnlySpan<ushort> aboveRow = picture.GetRowSpan(plane, y - 1); |
||||
|
int topStart = leftSampleCount + unitWidth; |
||||
|
for (int unit = 0; unit < aboveUnitCount; unit++) |
||||
|
{ |
||||
|
if (availability[cornerUnit + unit + 1]) |
||||
|
{ |
||||
|
aboveRow.Slice(x + (unit * unitWidth), unitWidth).CopyTo(line.Slice(topStart + (unit * unitWidth), unitWidth)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int firstAvailableUnit = availability.IndexOf(true); |
||||
|
int firstAvailableOffset = firstAvailableUnit < leftUnitCount |
||||
|
? firstAvailableUnit * unitHeight |
||||
|
: leftSampleCount + ((firstAvailableUnit - leftUnitCount) * unitWidth); |
||||
|
|
||||
|
int lineOffset = 0; |
||||
|
ushort precedingSample = line[firstAvailableOffset]; |
||||
|
for (int unit = 0; unit < totalUnitCount; unit++) |
||||
|
{ |
||||
|
int sampleCount = unit < leftUnitCount ? unitHeight : unitWidth; |
||||
|
Span<ushort> unitSamples = line.Slice(lineOffset, sampleCount); |
||||
|
if (!availability[unit]) |
||||
|
{ |
||||
|
unitSamples.Fill(precedingSample); |
||||
|
} |
||||
|
|
||||
|
precedingSample = unitSamples[^1]; |
||||
|
lineOffset += sampleCount; |
||||
|
} |
||||
|
|
||||
|
int cornerOffset = leftSampleCount + unitWidth - 1; |
||||
|
top[0] = left[0] = line[cornerOffset]; |
||||
|
line.Slice(topStart, size * 2).CopyTo(top[1..]); |
||||
|
for (int i = 1; i < referenceLength; i++) |
||||
|
{ |
||||
|
left[i] = line[leftSampleCount - i]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,154 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using BenchmarkDotNet.Attributes; |
||||
|
using SixLabors.ImageSharp.Formats.Heif.Hevc; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Benchmarks.Codecs.Heif; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Measures frame-wide preparation of complete and partially substituted HEVC intra references.
|
||||
|
/// </summary>
|
||||
|
[MemoryDiagnoser(displayGenColumns: false)] |
||||
|
public class HevcIntraReferencePreparationBenchmarks |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The coded frame width.
|
||||
|
/// </summary>
|
||||
|
private const int Width = 1920; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The coded frame height including the final padded coding-tree row for a 1080-line presentation.
|
||||
|
/// </summary>
|
||||
|
private const int Height = 1088; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The base-two logarithm of the benchmark prediction-block side.
|
||||
|
/// </summary>
|
||||
|
private const int BlockLog2 = 5; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The prediction-block side in samples.
|
||||
|
/// </summary>
|
||||
|
private const int BlockSize = 1 << BlockLog2; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The availability-unit side in samples.
|
||||
|
/// </summary>
|
||||
|
private const int UnitSize = 4; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The number of availability units surrounding one benchmark block.
|
||||
|
/// </summary>
|
||||
|
private const int AvailabilityUnitCount = ((BlockSize * 2) / UnitSize * 2) + 1; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The complete reference availability pattern.
|
||||
|
/// </summary>
|
||||
|
private readonly bool[] completeAvailability = new bool[AvailabilityUnitCount]; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The partial availability pattern exercising forward substitution.
|
||||
|
/// </summary>
|
||||
|
private readonly bool[] partialAvailability = new bool[AvailabilityUnitCount]; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The reusable top reference destination.
|
||||
|
/// </summary>
|
||||
|
private readonly ushort[] top = new ushort[(BlockSize * 2) + 1]; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The reusable left reference destination.
|
||||
|
/// </summary>
|
||||
|
private readonly ushort[] left = new ushort[(BlockSize * 2) + 1]; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The reusable reference-line scratch storage.
|
||||
|
/// </summary>
|
||||
|
private readonly ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(BlockLog2, UnitSize)]; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The reconstructed picture providing benchmark reference samples.
|
||||
|
/// </summary>
|
||||
|
private HevcPictureBuffer picture; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Allocates and initializes the reconstructed benchmark picture outside measured operations.
|
||||
|
/// </summary>
|
||||
|
[GlobalSetup] |
||||
|
public void Setup() |
||||
|
{ |
||||
|
this.picture = new HevcPictureBuffer( |
||||
|
Configuration.Default, |
||||
|
Width + (BlockSize * 2), |
||||
|
Height + (BlockSize * 2), |
||||
|
12, |
||||
|
12, |
||||
|
1, |
||||
|
false); |
||||
|
|
||||
|
for (int y = 0; y < this.picture.Height; y++) |
||||
|
{ |
||||
|
Span<ushort> row = this.picture.GetRowSpan(HevcPlane.Y, y); |
||||
|
for (int x = 0; x < row.Length; x++) |
||||
|
{ |
||||
|
row[x] = (ushort)(((37 * x) + (53 * y)) & 4095); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this.completeAvailability.AsSpan().Fill(true); |
||||
|
for (int i = 0; i < this.partialAvailability.Length; i++) |
||||
|
{ |
||||
|
this.partialAvailability[i] = (i & 3) != 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Releases the reconstructed benchmark picture.
|
||||
|
/// </summary>
|
||||
|
[GlobalCleanup] |
||||
|
public void Cleanup() => this.picture.Dispose(); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Measures the complete-border fast path across a padded 1080p coded frame.
|
||||
|
/// </summary>
|
||||
|
/// <returns>A prepared reference sample, keeping the result observable.</returns>
|
||||
|
[Benchmark(Baseline = true)] |
||||
|
public ushort PrepareCompleteFrame() => this.PrepareFrame(this.completeAvailability); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Measures partial-unit collection and substitution across a padded 1080p coded frame.
|
||||
|
/// </summary>
|
||||
|
/// <returns>A prepared reference sample, keeping the result observable.</returns>
|
||||
|
[Benchmark] |
||||
|
public ushort PreparePartialFrame() => this.PrepareFrame(this.partialAvailability); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Prepares references for every maximum-size block in the benchmark frame.
|
||||
|
/// </summary>
|
||||
|
/// <param name="availability">The repeated availability pattern.</param>
|
||||
|
/// <returns>The final prepared reference sample.</returns>
|
||||
|
private ushort PrepareFrame(ReadOnlySpan<bool> availability) |
||||
|
{ |
||||
|
for (int y = 0; y < Height; y += BlockSize) |
||||
|
{ |
||||
|
for (int x = 0; x < Width; x += BlockSize) |
||||
|
{ |
||||
|
HevcIntraPredictor.PrepareReferenceSamples( |
||||
|
this.picture, |
||||
|
HevcPlane.Y, |
||||
|
x + BlockSize, |
||||
|
y + BlockSize, |
||||
|
BlockLog2, |
||||
|
UnitSize, |
||||
|
UnitSize, |
||||
|
availability, |
||||
|
this.top, |
||||
|
this.left, |
||||
|
this.scratch); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return this.left[^1]; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,228 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Heif.Hevc; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies HEVC intra-reference collection, substitution, and filtering decisions.
|
||||
|
/// </summary>
|
||||
|
[Trait("Format", "Heic")] |
||||
|
public class HevcIntraReferenceSamplesTests |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Verifies that a fully available reference border is copied directly from the reconstructed plane.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void PrepareReferenceSamplesCopiesCompleteBorder() |
||||
|
{ |
||||
|
const int blockX = 8; |
||||
|
const int blockY = 8; |
||||
|
const int log2Size = 2; |
||||
|
const int unitSize = 2; |
||||
|
const int referenceLength = 9; |
||||
|
using HevcPictureBuffer picture = CreatePicture(32, 32, 10, 1); |
||||
|
bool[] availableUnits = new bool[9]; |
||||
|
availableUnits.AsSpan().Fill(true); |
||||
|
ushort[] top = new ushort[referenceLength]; |
||||
|
ushort[] left = new ushort[referenceLength]; |
||||
|
ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(log2Size, unitSize)]; |
||||
|
|
||||
|
HevcIntraPredictor.PrepareReferenceSamples( |
||||
|
picture, |
||||
|
HevcPlane.Y, |
||||
|
blockX, |
||||
|
blockY, |
||||
|
log2Size, |
||||
|
unitSize, |
||||
|
unitSize, |
||||
|
availableUnits, |
||||
|
top, |
||||
|
left, |
||||
|
scratch); |
||||
|
|
||||
|
for (int i = 0; i < referenceLength; i++) |
||||
|
{ |
||||
|
Assert.Equal(GetSample(blockX + i - 1, blockY - 1), top[i]); |
||||
|
Assert.Equal(GetSample(blockX - 1, blockY + i - 1), left[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies that a block without available neighbors receives the component midpoint.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void PrepareReferenceSamplesUsesMidpointWithoutNeighbors() |
||||
|
{ |
||||
|
const int log2Size = 2; |
||||
|
const int unitSize = 2; |
||||
|
const ushort midpoint = 512; |
||||
|
using HevcPictureBuffer picture = CreatePicture(16, 16, 10, 1); |
||||
|
bool[] availableUnits = new bool[9]; |
||||
|
ushort[] top = new ushort[9]; |
||||
|
ushort[] left = new ushort[9]; |
||||
|
ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(log2Size, unitSize)]; |
||||
|
|
||||
|
HevcIntraPredictor.PrepareReferenceSamples( |
||||
|
picture, |
||||
|
HevcPlane.Y, |
||||
|
0, |
||||
|
0, |
||||
|
log2Size, |
||||
|
unitSize, |
||||
|
unitSize, |
||||
|
availableUnits, |
||||
|
top, |
||||
|
left, |
||||
|
scratch); |
||||
|
|
||||
|
Assert.All(top, sample => Assert.Equal(midpoint, sample)); |
||||
|
Assert.All(left, sample => Assert.Equal(midpoint, sample)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies forward substitution across unavailable below-left, left, corner, top, and above-right units.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void PrepareReferenceSamplesSubstitutesPartialBorderInNormativeOrder() |
||||
|
{ |
||||
|
const int log2Size = 2; |
||||
|
const int unitSize = 2; |
||||
|
using HevcPictureBuffer picture = CreatePicture(32, 32, 10, 1); |
||||
|
bool[] availableUnits = [false, true, false, true, false, false, true, false, false]; |
||||
|
ushort[] top = new ushort[9]; |
||||
|
ushort[] left = new ushort[9]; |
||||
|
ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(log2Size, unitSize)]; |
||||
|
|
||||
|
HevcIntraPredictor.PrepareReferenceSamples( |
||||
|
picture, |
||||
|
HevcPlane.Y, |
||||
|
8, |
||||
|
8, |
||||
|
log2Size, |
||||
|
unitSize, |
||||
|
unitSize, |
||||
|
availableUnits, |
||||
|
top, |
||||
|
left, |
||||
|
scratch); |
||||
|
|
||||
|
ushort[] expectedTop = [807, 807, 807, 710, 711, 711, 711, 711, 711]; |
||||
|
ushort[] expectedLeft = [807, 807, 907, 1207, 1207, 1207, 1307, 1307, 1307]; |
||||
|
Assert.True(expectedTop.AsSpan().SequenceEqual(top)); |
||||
|
Assert.True(expectedLeft.AsSpan().SequenceEqual(left)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies asymmetric availability units used by horizontally subsampled 4:2:2 chroma planes.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void PrepareReferenceSamplesSupportsAsymmetricChromaUnits() |
||||
|
{ |
||||
|
const int blockX = 16; |
||||
|
const int blockY = 16; |
||||
|
const int log2Size = 3; |
||||
|
const int unitWidth = 2; |
||||
|
const int unitHeight = 4; |
||||
|
const int referenceLength = 17; |
||||
|
using HevcPictureBuffer picture = CreatePicture(48, 48, 10, 2); |
||||
|
bool[] availableUnits = [true, false, false, false, false, true, false, false, false, false, false, false, false]; |
||||
|
ushort[] top = new ushort[referenceLength]; |
||||
|
ushort[] left = new ushort[referenceLength]; |
||||
|
ushort[] scratch = new ushort[HevcIntraPredictor.GetReferenceScratchLength(log2Size, unitWidth)]; |
||||
|
|
||||
|
HevcIntraPredictor.PrepareReferenceSamples( |
||||
|
picture, |
||||
|
HevcPlane.Cb, |
||||
|
blockX, |
||||
|
blockY, |
||||
|
log2Size, |
||||
|
unitWidth, |
||||
|
unitHeight, |
||||
|
availableUnits, |
||||
|
top, |
||||
|
left, |
||||
|
scratch); |
||||
|
|
||||
|
ushort substitutedLeft = GetSample(blockX - 1, blockY + 12); |
||||
|
Assert.Equal(substitutedLeft, top[0]); |
||||
|
Assert.Equal(GetSample(blockX, blockY - 1), top[1]); |
||||
|
Assert.Equal(GetSample(blockX + 1, blockY - 1), top[2]); |
||||
|
for (int i = 3; i < referenceLength; i++) |
||||
|
{ |
||||
|
Assert.Equal(top[2], top[i]); |
||||
|
} |
||||
|
|
||||
|
for (int i = 0; i <= 13; i++) |
||||
|
{ |
||||
|
Assert.Equal(substitutedLeft, left[i]); |
||||
|
} |
||||
|
|
||||
|
Assert.Equal(GetSample(blockX - 1, blockY + 13), left[14]); |
||||
|
Assert.Equal(GetSample(blockX - 1, blockY + 14), left[15]); |
||||
|
Assert.Equal(GetSample(blockX - 1, blockY + 15), left[16]); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Verifies the mode, size, component, chroma-format, and sequence controls for reference filtering.
|
||||
|
/// </summary>
|
||||
|
[Fact] |
||||
|
public void ShouldFilterReferenceSamplesUsesHevcThresholds() |
||||
|
{ |
||||
|
Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 0, 2, 1, false)); |
||||
|
Assert.True(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 0, 3, 1, false)); |
||||
|
Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 1, 5, 1, false)); |
||||
|
Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 26, 5, 1, false)); |
||||
|
Assert.True(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 2, 3, 1, false)); |
||||
|
Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Cb, 2, 3, 1, false)); |
||||
|
Assert.True(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Cb, 2, 3, 3, false)); |
||||
|
Assert.False(HevcIntraPredictor.ShouldFilterReferenceSamples(HevcPlane.Y, 2, 3, 1, true)); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Creates a picture whose luma and chroma samples encode their source coordinates.
|
||||
|
/// </summary>
|
||||
|
/// <param name="width">The coded luma width.</param>
|
||||
|
/// <param name="height">The coded luma height.</param>
|
||||
|
/// <param name="bitDepth">The component precision.</param>
|
||||
|
/// <param name="chromaFormat">The HEVC chroma-format identifier.</param>
|
||||
|
/// <returns>The initialized picture.</returns>
|
||||
|
private static HevcPictureBuffer CreatePicture(int width, int height, int bitDepth, byte chromaFormat) |
||||
|
{ |
||||
|
HevcPictureBuffer picture = new(Configuration.Default, width, height, bitDepth, bitDepth, chromaFormat, false); |
||||
|
FillPlane(picture, HevcPlane.Y); |
||||
|
if (chromaFormat != 0) |
||||
|
{ |
||||
|
FillPlane(picture, HevcPlane.Cb); |
||||
|
FillPlane(picture, HevcPlane.Cr); |
||||
|
} |
||||
|
|
||||
|
return picture; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Fills one picture plane with coordinate-derived samples.
|
||||
|
/// </summary>
|
||||
|
/// <param name="picture">The destination picture.</param>
|
||||
|
/// <param name="plane">The destination plane.</param>
|
||||
|
private static void FillPlane(HevcPictureBuffer picture, HevcPlane plane) |
||||
|
{ |
||||
|
for (int y = 0; y < picture.GetHeight(plane); y++) |
||||
|
{ |
||||
|
Span<ushort> row = picture.GetRowSpan(plane, y); |
||||
|
for (int x = 0; x < row.Length; x++) |
||||
|
{ |
||||
|
row[x] = GetSample(x, y); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the deterministic sample value for one plane coordinate.
|
||||
|
/// </summary>
|
||||
|
/// <param name="x">The sample X coordinate.</param>
|
||||
|
/// <param name="y">The sample Y coordinate.</param>
|
||||
|
/// <returns>The coordinate-derived sample.</returns>
|
||||
|
private static ushort GetSample(int x, int y) => (ushort)((y * 100) + x); |
||||
|
} |
||||
Loading…
Reference in new issue