mirror of https://github.com/SixLabors/ImageSharp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
5.2 KiB
138 lines
5.2 KiB
// Copyright (c) Six Labors.
|
|
// Licensed under the Six Labors Split License.
|
|
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1;
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
|
|
using SixLabors.ImageSharp.Tests.TestUtilities;
|
|
|
|
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
|
|
|
|
/// <summary>
|
|
/// Verifies AV1 palette reconstruction across every supported sample precision and intrinsic tier.
|
|
/// </summary>
|
|
[Trait("Format", "Avif")]
|
|
public class Av1PalettePredictorTests
|
|
{
|
|
/// <summary>
|
|
/// The hardware configurations required to exercise each packed width and the scalar fallback.
|
|
/// </summary>
|
|
private const HwIntrinsics Configurations =
|
|
HwIntrinsics.AllowAll | HwIntrinsics.DisableAVX512F | HwIntrinsics.DisableAVX | HwIntrinsics.DisableHWIntrinsic;
|
|
|
|
/// <summary>
|
|
/// Verifies exact indexed reconstruction and destination-padding preservation for every palette size.
|
|
/// </summary>
|
|
[Fact]
|
|
public void PredictMatchesIndependentDefinitionAcrossIntrinsicWidths()
|
|
=> FeatureTestRunner.RunWithHwIntrinsicsFeature(ValidatePredictors, Configurations);
|
|
|
|
/// <summary>
|
|
/// Exercises all palette sizes and transform widths at 8, 10, and 12 bits.
|
|
/// </summary>
|
|
private static void ValidatePredictors()
|
|
{
|
|
int[] widths = [4, 8, 16, 32, 64];
|
|
foreach (int paletteSize in Enumerable.Range(2, Av1Constants.PaletteMaxSize - 1))
|
|
{
|
|
foreach (int width in widths)
|
|
{
|
|
int height = width == 64 ? 16 : width;
|
|
int mapStride = width + 5;
|
|
int destinationStride = width + 9;
|
|
byte[] colorIndexMap = CreateColorIndexMap(mapStride, height, width, paletteSize);
|
|
ushort[] bytePalette = CreatePalette(paletteSize, 8);
|
|
byte[] expectedBytes = Enumerable.Repeat((byte)251, destinationStride * height).ToArray();
|
|
byte[] actualBytes = (byte[])expectedBytes.Clone();
|
|
|
|
ApplyReference(bytePalette, colorIndexMap, mapStride, expectedBytes, destinationStride, width, height);
|
|
Av1PalettePredictor.Predict(bytePalette, colorIndexMap, mapStride, actualBytes, destinationStride, width, height);
|
|
Assert.Equal(expectedBytes, actualBytes);
|
|
|
|
foreach (int bitDepth in new[] { 10, 12 })
|
|
{
|
|
ushort[] palette = CreatePalette(paletteSize, bitDepth);
|
|
short[] expected = Enumerable.Repeat((short)-1, destinationStride * height).ToArray();
|
|
short[] actual = (short[])expected.Clone();
|
|
|
|
ApplyReference(palette, colorIndexMap, mapStride, expected, destinationStride, width, height);
|
|
Av1PalettePredictor.Predict(palette, colorIndexMap, mapStride, actual, destinationStride, width, height);
|
|
Assert.Equal(expected, actual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a deterministic palette spanning the legal range for the requested bit depth.
|
|
/// </summary>
|
|
private static ushort[] CreatePalette(int paletteSize, int bitDepth)
|
|
{
|
|
ushort[] result = new ushort[paletteSize];
|
|
int maximum = (1 << bitDepth) - 1;
|
|
for (int index = 0; index < result.Length; index++)
|
|
{
|
|
result[index] = (ushort)(((index * 977) + 37) & maximum);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates deterministic active indices and invalid padding indices for each map row.
|
|
/// </summary>
|
|
private static byte[] CreateColorIndexMap(int stride, int height, int width, int paletteSize)
|
|
{
|
|
byte[] result = Enumerable.Repeat((byte)Av1Constants.PaletteMaxSize, stride * height).ToArray();
|
|
for (int row = 0; row < height; row++)
|
|
{
|
|
for (int column = 0; column < width; column++)
|
|
{
|
|
result[(row * stride) + column] = (byte)(((row * 5) + (column * 3)) % paletteSize);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies independent scalar palette lookup to an 8-bit destination.
|
|
/// </summary>
|
|
private static void ApplyReference(
|
|
ReadOnlySpan<ushort> palette,
|
|
ReadOnlySpan<byte> colorIndexMap,
|
|
int mapStride,
|
|
Span<byte> destination,
|
|
int destinationStride,
|
|
int width,
|
|
int height)
|
|
{
|
|
for (int row = 0; row < height; row++)
|
|
{
|
|
for (int column = 0; column < width; column++)
|
|
{
|
|
destination[(row * destinationStride) + column] = (byte)palette[colorIndexMap[(row * mapStride) + column]];
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies independent scalar palette lookup to a high-bit-depth destination.
|
|
/// </summary>
|
|
private static void ApplyReference(
|
|
ReadOnlySpan<ushort> palette,
|
|
ReadOnlySpan<byte> colorIndexMap,
|
|
int mapStride,
|
|
Span<short> destination,
|
|
int destinationStride,
|
|
int width,
|
|
int height)
|
|
{
|
|
for (int row = 0; row < height; row++)
|
|
{
|
|
for (int column = 0; column < width; column++)
|
|
{
|
|
destination[(row * destinationStride) + column] = (short)palette[colorIndexMap[(row * mapStride) + column]];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|