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