// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Heif.Av1;
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
///
/// Validates complete AV1 reconstruction against independently decoded native component planes.
///
[Trait("Format", "Avif")]
public class Av1DeblockingConformanceTests
{
///
/// Verifies deblocking syntax, filter activation, component traversal, and presentation for real eight-, ten-,
/// and twelve-bit AV1 and AVIF content.
///
[Fact]
public void DecodeMatchesPinnedLibaomReference()
{
ValidateFixture(
TestImages.Heif.Av1Deblocking8BitAvif,
TestImages.Heif.Av1Deblocking8BitPayload,
TestImages.Heif.Av1Deblocking8BitReference,
768,
512,
Av1BitDepth.EightBit,
Av1ColorFormat.Yuv420,
HeifBitDepth.Bit8);
ValidateFixture(
TestImages.Heif.Av1Deblocking10BitAvif,
TestImages.Heif.Av1Deblocking10BitPayload,
TestImages.Heif.Av1Deblocking10BitReference,
1024,
428,
Av1BitDepth.TenBit,
Av1ColorFormat.Yuv444,
HeifBitDepth.Bit10);
ValidateNativeFixture(
TestImages.Heif.Av1Deblocking12BitPayload,
TestImages.Heif.Av1Deblocking12BitReference,
1024,
428,
Av1BitDepth.TwelveBit,
Av1ColorFormat.Yuv444);
ValidatePresentedImage(TestImages.Heif.Av1Deblocking12BitAvif, 64, 64, HeifBitDepth.Bit12);
}
///
/// Validates one elementary-stream sample and its containing AVIF image.
///
/// The complete AVIF container.
/// The AV1 elementary-stream sample extracted from the container.
/// The native planar output produced by the pinned libaom decoder.
/// The expected displayed width.
/// The expected displayed height.
/// The expected AV1 sample precision.
/// The expected native chroma-sampling layout.
/// The expected public HEIF sample precision.
private static void ValidateFixture(
string imagePath,
string payloadPath,
string referencePath,
int width,
int height,
Av1BitDepth bitDepth,
Av1ColorFormat colorFormat,
HeifBitDepth metadataBitDepth)
{
ValidateNativeFixture(payloadPath, referencePath, width, height, bitDepth, colorFormat);
ValidatePresentedImage(imagePath, width, height, metadataBitDepth);
}
///
/// Validates complete native-plane reconstruction for one AV1 elementary-stream sample.
///
/// The AV1 elementary-stream sample.
/// The native planar output produced by the pinned libaom decoder.
/// The expected reconstructed width.
/// The expected reconstructed height.
/// The expected AV1 sample precision.
/// The expected native chroma-sampling layout.
private static void ValidateNativeFixture(
string payloadPath,
string referencePath,
int width,
int height,
Av1BitDepth bitDepth,
Av1ColorFormat colorFormat)
{
byte[] payload = TestFile.Create(payloadPath).Bytes;
byte[] reference = TestFile.Create(referencePath).Bytes;
using Av1Decoder decoder = new(Configuration.Default);
using Av1FrameBuffer frameBuffer = decoder.DecodeFrameBuffer(payload, null, null, out _);
Assert.Equal(width, frameBuffer.Width);
Assert.Equal(height, frameBuffer.Height);
Assert.Equal(bitDepth, frameBuffer.BitDepth);
Assert.Equal(colorFormat, frameBuffer.ColorFormat);
Assert.NotNull(decoder.FrameHeader);
ObuLoopFilterParameters filterParameters = decoder.FrameHeader.LoopFilterParameters;
Assert.True(
filterParameters.FilterLevel[0] != 0
|| filterParameters.FilterLevel[1] != 0
|| filterParameters.FilterLevelU != 0
|| filterParameters.FilterLevelV != 0);
AssertNativePlanesEqual(frameBuffer, reference);
}
///
/// Validates the public presentation and metadata produced from one complete AVIF container.
///
/// The complete AVIF container.
/// The expected displayed width.
/// The expected displayed height.
/// The expected public HEIF sample precision.
private static void ValidatePresentedImage(string imagePath, int width, int height, HeifBitDepth metadataBitDepth)
{
DecoderOptions options = new() { MaxFrames = 1 };
byte[] imageBytes = TestFile.Create(imagePath).Bytes;
using Image image = Image.Load(options, imageBytes);
Assert.Equal(width, image.Width);
Assert.Equal(height, image.Height);
Assert.Single(image.Frames);
HeifMetadata metadata = image.Metadata.GetHeifMetadata();
Assert.Equal(HeifCompressionMethod.Av1, metadata.CompressionMethod);
Assert.Equal(metadataBitDepth, metadata.BitDepth);
}
///
/// Compares every visible native component sample with the independent planar reference.
///
/// The reconstructed AV1 component planes.
/// The planar Y, U, and V samples produced by the pinned libaom decoder.
private static void AssertNativePlanesEqual(Av1FrameBuffer frameBuffer, ReadOnlySpan reference)
{
(int chromaSubsamplingX, int chromaSubsamplingY) = frameBuffer.ColorFormat switch
{
Av1ColorFormat.Yuv420 => (1, 1),
Av1ColorFormat.Yuv422 => (1, 0),
_ => (0, 0)
};
int referenceOffset = 0;
foreach (Av1Plane plane in new[] { Av1Plane.Y, Av1Plane.U, Av1Plane.V })
{
int subsamplingX = plane == Av1Plane.Y ? 0 : chromaSubsamplingX;
int subsamplingY = plane == Av1Plane.Y ? 0 : chromaSubsamplingY;
int planeWidth = GetSubsampledSize(frameBuffer.Width, subsamplingX);
int planeHeight = GetSubsampledSize(frameBuffer.Height, subsamplingY);
if (frameBuffer.BitDepth == Av1BitDepth.EightBit)
{
Buffer2DRegion actualPlane = frameBuffer.DeriveBlockPointer(plane, subsamplingX, subsamplingY);
for (int y = 0; y < planeHeight; y++)
{
Span actualRow = actualPlane.DangerousGetRowSpan(y)[..planeWidth];
ReadOnlySpan expectedRow = reference.Slice(referenceOffset, planeWidth);
for (int x = 0; x < planeWidth; x++)
{
AssertSampleEqual(plane, x, y, expectedRow[x], actualRow[x]);
}
referenceOffset += planeWidth;
}
}
else
{
// aomdec writes high-bit-depth YUV as little-endian 16-bit values, independently of host endianness.
for (int y = 0; y < planeHeight; y++)
{
Span actualRow = frameBuffer.GetHighBitDepthRowSpan(plane, y, subsamplingX, subsamplingY);
for (int x = 0; x < planeWidth; x++)
{
ushort expected = BinaryPrimitives.ReadUInt16LittleEndian(reference.Slice(referenceOffset, sizeof(ushort)));
AssertSampleEqual(plane, x, y, expected, actualRow[x]);
referenceOffset += sizeof(ushort);
}
}
}
}
Assert.Equal(reference.Length, referenceOffset);
}
///
/// Calculates a component dimension after chroma subsampling with the AV1 rounding rule.
///
/// The luma dimension.
/// The component subsampling shift.
/// The subsampled component dimension.
private static int GetSubsampledSize(int size, int subsampling)
=> (size + (1 << subsampling) - 1) >> subsampling;
///
/// Reports the exact component coordinate when independently decoded samples differ.
///
/// The compared component plane.
/// The sample X coordinate.
/// The sample Y coordinate.
/// The reference sample.
/// The reconstructed sample.
private static void AssertSampleEqual(Av1Plane plane, int x, int y, ushort expected, ushort actual)
{
if (expected != actual)
{
Assert.Fail($"Plane {plane} differs at ({x}, {y}): expected {expected}, actual {actual}.");
}
}
}