Browse Source

Implement HEVC intra reference preparation

pull/2633/head
James Jackson-South 2 months ago
parent
commit
29db1164ad
  1. 7
      HEIF_IMPLEMENTATION_PLAN.md
  2. 174
      src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.References.cs
  3. 7
      src/ImageSharp/Formats/Heif/Hevc/HevcPictureBuffer.cs
  4. 154
      tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraReferencePreparationBenchmarks.cs
  5. 228
      tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraReferenceSamplesTests.cs

7
HEIF_IMPLEMENTATION_PLAN.md

@ -43,7 +43,8 @@ Checkboxes may be marked complete only when the implementation and the verificat
- [x] Move the H.273 parameter resolver, closed color operators, transfer functions, sample loading/storage, chroma filtering, and RGB packing into one shared HEIF color pipeline used by both AV1 and HEVC rather than maintaining codec-specific arithmetic copies.
- [x] Implement sequential pooled HEVC conversion in both directions for monochrome, 4:2:0, 4:2:2, and 4:4:4; independent luma/chroma precision; 8/10/12-bit full/limited ranges; and all six progressive 4:2:0 chroma sample locations. Eight-bit paths use JPEG's optimized RGB plane contracts, while high-bit-depth paths retain 16-bit packed pixels.
- [x] Implement allocation-free SIMD-first HEVC intra prediction for all 35 modes, 4/8/16/32 blocks, and 8/10/12-bit samples, including three-tap and strong-bilinear reference filtering, negative-angle reference extension, luma boundary filters, and SIMD horizontal transposition. Verify the normal and forced-scalar paths against one scalar oracle.
- [ ] Connect prediction to coding-tree and transform-unit traversal, reconstructed-plane reference availability, and the bounded item decoder lifecycle.
- [x] Implement allocation-free reconstructed-plane reference collection and normative substitution for complete, empty, and partially available borders, including asymmetric 4:2:2 availability units and the HEVC mode/size/component filter-selection rules.
- [ ] Derive each reference-unit availability flag from coding-tree, slice, tile, constrained-intra, and picture-boundary state, then connect prediction to transform-unit traversal and the bounded item decoder lifecycle.
- [ ] Complete the SIMD YUV/CICP paths for every supported AV1 bit depth, chroma format, range, matrix, transfer function, color primary, and chroma position, with scalar fallback only when hardware vectorization is unavailable or the operation is inherently non-vectorizable.
- [x] Apply ICC conversion only after the SIMD YUV/CICP stage, alpha composition, grid assembly, and presentation transforms have produced the presented RGB image; retain ImageSharp's shared ICC converter and optimize reusable bulk kernels rather than creating a HEIF-specific color-management implementation.
- [ ] Verify ICC preservation, conversion, compaction, and metadata skipping for grids, alpha-composited images, every presented sequence frame, and the completed HEVC path.
@ -462,7 +463,8 @@ Implement and verify in dependency order:
- [ ] Coding-tree, coding-unit, prediction-unit, and transform-unit traversal across all permitted sizes and partition modes.
- [ ] Intra prediction for every luma and chroma mode, including strong intra smoothing and constrained prediction rules.
- [x] Implement and verify the allocation-free SIMD-first predictor primitive for all 35 modes, 4/8/16/32 blocks, and 8/10/12-bit samples.
- [ ] Build reference availability from reconstructed-plane and coding state, and connect the predictor to transform-unit traversal.
- [x] Implement reconstructed-plane reference collection, substitution, asymmetric chroma-unit handling, and reference-filter selection from caller-owned availability and scratch spans.
- [ ] Build each availability flag from reconstructed-plane and coding state, and connect the predictor to transform-unit traversal.
- [ ] Scaling lists, inverse quantization, transform skip, every required inverse transform, range-extension precision, and lossless reconstruction.
- [ ] Deblocking and sample-adaptive offset for every signaled luma/chroma and bit-depth path.
- [ ] Tiles, wavefront entry points, dependent slices, and all other parallelization syntax permitted by the exposed still-image profiles.
@ -558,6 +560,7 @@ Tasks:
- [x] Complete the active AV1 forward and inverse transform-family checklist above.
- [ ] Benchmark codec-specific costs for CABAC/range decode, inverse transforms, still-image prediction, deblocking, SAO, CDEF, restoration, chroma upsampling, color conversion, alpha packing, and grid copies.
- [x] Add a permanent frame-wide HEVC intra-prediction benchmark. On .NET 10, SIMD planar, vertical-angular, and horizontal-angular prediction measured 155.2, 150.7, and 255.0 microseconds per padded 1920x1088 frame, compared with forced-scalar timings of 1.890, 1.081, and 0.979 milliseconds: 12.2, 7.2, and 3.8 times faster with zero managed allocations.
- [x] Add a permanent frame-wide HEVC reference-preparation benchmark. On .NET 10, complete and partially substituted borders measured 465.3 and 566.7 microseconds per 2,040-block padded 1920x1088 frame, compared with forced-scalar timings of 477.5 and 608.0 microseconds, with zero managed allocations.
- [ ] Implement vector paths only for confirmed hot loops, using existing `Vector128`, `Vector256`, and `Vector512` helper and dispatch patterns where supported.
- [ ] Prioritize shared color conversion and pixel packing, chroma upsampling, inverse-transform add-and-clip, intra predictors, HEVC deblock/SAO, AV1 loop filter/CDEF/restoration, and contiguous grid copies.
- [ ] Benchmark the complete decode color pipeline on representative 8/10/12-bit AVIF and HEIC images with and without embedded ICC profiles. Report absolute end-to-end timings and allocations in addition to the isolated YUV/CICP and ICC stage costs.

174
src/ImageSharp/Formats/Heif/Hevc/HevcIntraPredictor.References.cs

@ -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];
}
}
}

7
src/ImageSharp/Formats/Heif/Hevc/HevcPictureBuffer.cs

@ -136,6 +136,13 @@ internal sealed class HevcPictureBuffer : IDisposable
/// <returns>Zero for luma and full-resolution planes; otherwise, the chroma shift.</returns>
public int GetSubsamplingY(HevcPlane plane) => plane == HevcPlane.Y ? 0 : this.chromaSubsamplingY;
/// <summary>
/// Gets the sample precision for the selected reconstruction plane.
/// </summary>
/// <param name="plane">The reconstruction plane.</param>
/// <returns>The plane sample precision in bits.</returns>
public int GetBitDepth(HevcPlane plane) => plane == HevcPlane.Y ? this.BitDepthLuma : this.BitDepthChroma;
/// <summary>
/// Gets the selected plane width in samples.
/// </summary>

154
tests/ImageSharp.Benchmarks/Codecs/Heif/HevcIntraReferencePreparationBenchmarks.cs

@ -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];
}
}

228
tests/ImageSharp.Tests/Formats/Heif/Hevc/HevcIntraReferenceSamplesTests.cs

@ -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…
Cancel
Save