// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif;
using SixLabors.ImageSharp.Formats.Heif.Av1;
namespace SixLabors.ImageSharp.Tests.Formats.Heif;
///
/// Verifies parsing and interpretation of HEIF image-item property payloads.
///
[Trait("Format", "Heif")]
public class HeifPropertyParserTests
{
///
/// Verifies that every AV1 operating-point index representable by a sequence header is accepted.
///
/// The zero-based operating-point index.
[Theory]
[InlineData(0)]
[InlineData(31)]
public void ParseAv1OperatingPointSelectorAcceptsSequenceHeaderRange(byte index)
{
Av1OperatingPointSelector selector = HeifPropertyParser.ParseAv1OperatingPointSelector([index]);
Assert.Equal(index, selector.Index);
}
///
/// Verifies that an AV1 operating-point index outside the sequence-header range is rejected.
///
[Fact]
public void ParseAv1OperatingPointSelectorRejectsOutOfRangeIndex()
=> Assert.Throws(() => HeifPropertyParser.ParseAv1OperatingPointSelector([32]));
///
/// Verifies the four AV1 spatial-layer identifiers and the progressive final-layer selector.
///
/// The most-significant selector byte.
/// The least-significant selector byte.
/// The parsed layer identifier.
[Theory]
[InlineData(0, 0, 0)]
[InlineData(0, 3, 3)]
[InlineData(255, 255, Av1LayerSelector.AllLayers)]
public void ParseAv1LayerSelectorAcceptsDefinedValues(byte highByte, byte lowByte, ushort expected)
{
Av1LayerSelector selector = HeifPropertyParser.ParseAv1LayerSelector([highByte, lowByte]);
Assert.Equal(expected, selector.LayerId);
}
///
/// Verifies that an AV1 spatial-layer identifier wider than the OBU extension field is rejected.
///
[Fact]
public void ParseAv1LayerSelectorRejectsOutOfRangeLayer()
=> Assert.Throws(() => HeifPropertyParser.ParseAv1LayerSelector([0, 4]));
///
/// Verifies the compact 16-bit representation of the three explicit layered-image boundaries.
///
[Fact]
public void ParseAv1LayeredImageIndexReadsSmallSizes()
{
Av1LayeredImageIndex index = HeifPropertyParser.ParseAv1LayeredImageIndex([0, 0, 55, 0, 17, 1, 2]);
Assert.Equal(55U, index.FirstLayerSize);
Assert.Equal(17U, index.SecondLayerSize);
Assert.Equal(258U, index.ThirdLayerSize);
}
///
/// Verifies the 32-bit representation used when a layered-image boundary exceeds 16 bits.
///
[Fact]
public void ParseAv1LayeredImageIndexReadsLargeSizes()
{
Av1LayeredImageIndex index = HeifPropertyParser.ParseAv1LayeredImageIndex(
[1, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0]);
Assert.Equal(65536U, index.FirstLayerSize);
Assert.Equal(131072U, index.SecondLayerSize);
Assert.Equal(196608U, index.ThirdLayerSize);
}
///
/// Verifies that reserved layered-image flag bits cannot alter the payload interpretation.
///
[Fact]
public void ParseAv1LayeredImageIndexRejectsReservedBits()
=> Assert.Throws(() => HeifPropertyParser.ParseAv1LayeredImageIndex([2, 0, 1, 0, 0, 0, 0]));
///
/// Verifies cumulative selection through the two-layer payload used by the libavif progressive fixture.
///
/// The selected spatial layer.
/// The cumulative payload length through that layer.
[Theory]
[InlineData(0, 55)]
[InlineData(1, 72)]
[InlineData(Av1LayerSelector.AllLayers, 72)]
public void GetPayloadLengthSelectsCumulativeLayerBytes(ushort layerId, int expectedLength)
{
Av1LayeredImageIndex index = new(55, 0, 0);
Av1LayerSelector selector = new(layerId);
Assert.Equal(expectedLength, index.GetPayloadLength(72, selector));
}
///
/// Verifies that a selector cannot address a layer absent from the indexed item payload.
///
[Fact]
public void GetPayloadLengthRejectsAbsentLayer()
{
Av1LayeredImageIndex index = new(55, 0, 0);
Av1LayerSelector selector = new(2);
Assert.Throws(() => index.GetPayloadLength(72, selector));
}
///
/// Verifies that every explicit layer boundary leaves bytes for the following implicit layer.
///
[Fact]
public void GetPayloadLengthRejectsBoundaryAtItemEnd()
{
Av1LayeredImageIndex index = new(72, 0, 0);
Assert.Throws(() => index.GetPayloadLength(72, null));
}
}