// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Hevc;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Hevc;
///
/// Verifies HEVC intra-reference collection, substitution, and filtering decisions.
///
[Trait("Format", "Heic")]
public class HevcIntraReferenceSamplesTests
{
///
/// Verifies that a fully available reference border is copied directly from the reconstructed plane.
///
[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]);
}
}
///
/// Verifies that a block without available neighbors receives the component midpoint.
///
[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));
}
///
/// Verifies forward substitution across unavailable below-left, left, corner, top, and above-right units.
///
[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));
}
///
/// Verifies asymmetric availability units used by horizontally subsampled 4:2:2 chroma planes.
///
[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]);
}
///
/// Verifies the mode, size, component, chroma-format, and sequence controls for reference filtering.
///
[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));
}
///
/// Creates a picture whose luma and chroma samples encode their source coordinates.
///
/// The coded luma width.
/// The coded luma height.
/// The component precision.
/// The HEVC chroma-format identifier.
/// The initialized picture.
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;
}
///
/// Fills one picture plane with coordinate-derived samples.
///
/// The destination picture.
/// The destination plane.
private static void FillPlane(HevcPictureBuffer picture, HevcPlane plane)
{
for (int y = 0; y < picture.GetHeight(plane); y++)
{
Span row = picture.GetRowSpan(plane, y);
for (int x = 0; x < row.Length; x++)
{
row[x] = GetSample(x, y);
}
}
}
///
/// Gets the deterministic sample value for one plane coordinate.
///
/// The sample X coordinate.
/// The sample Y coordinate.
/// The coordinate-derived sample.
private static ushort GetSample(int x, int y) => (ushort)((y * 100) + x);
}