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.
151 lines
5.6 KiB
151 lines
5.6 KiB
// Copyright (c) Six Labors.
|
|
// Licensed under the Six Labors Split License.
|
|
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1;
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1.OpenBitstreamUnit;
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction.ChromaFromLuma;
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling;
|
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
|
|
|
|
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
|
|
|
|
[Trait("Format", "Avif")]
|
|
public class Av1ChromaFromLumaTests
|
|
{
|
|
[Theory]
|
|
[InlineData(false, false, new short[] { 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 })]
|
|
[InlineData(true, false, new short[] { 12, 28, 44, 60, 76, 92, 108, 124 })]
|
|
[InlineData(true, true, new short[] { 28, 44, 92, 108 })]
|
|
public void Store8BitMatchesLibaomSubsampling(bool subX, bool subY, short[] expected)
|
|
{
|
|
ObuColorConfig colorConfig = new() { SubSamplingX = subX, SubSamplingY = subY };
|
|
Av1ChromaFromLumaContext context = new(colorConfig);
|
|
byte[] input = Enumerable.Range(1, 16).Select(x => (byte)x).ToArray();
|
|
|
|
context.Store(input, 4, 0, 0, Av1TransformSize.Size4x4, Av1BlockSize.Block4x4, 0, 0);
|
|
|
|
int width = 4 >> (subX ? 1 : 0);
|
|
int height = 4 >> (subY ? 1 : 0);
|
|
Assert.Equal(expected, GetBlock(context.Q3Buffer, width, height));
|
|
}
|
|
|
|
[Fact]
|
|
public void StoreHighBitDepthPreservesTwelveBitQ3Range()
|
|
{
|
|
ObuColorConfig colorConfig = new();
|
|
Av1ChromaFromLumaContext context = new(colorConfig);
|
|
short[] input = Enumerable.Repeat((short)4095, 16).ToArray();
|
|
|
|
context.Store(input, 4, 0, 0, Av1TransformSize.Size4x4, Av1BlockSize.Block4x4, 0, 0);
|
|
|
|
Assert.All(GetBlock(context.Q3Buffer, 4, 4), value => Assert.Equal(32760, value));
|
|
}
|
|
|
|
[Fact]
|
|
public void StoreCombinesSub8x8LumaBeforeSubtractingAverage()
|
|
{
|
|
ObuColorConfig colorConfig = new() { SubSamplingX = true, SubSamplingY = true };
|
|
Av1ChromaFromLumaContext context = new(colorConfig);
|
|
|
|
context.Store(Enumerable.Repeat((byte)10, 16).ToArray(), 4, 0, 0, Av1TransformSize.Size4x4, Av1BlockSize.Block4x4, 0, 0);
|
|
context.Store(Enumerable.Repeat((byte)20, 16).ToArray(), 4, 0, 0, Av1TransformSize.Size4x4, Av1BlockSize.Block4x4, 0, 1);
|
|
context.Store(Enumerable.Repeat((byte)30, 16).ToArray(), 4, 0, 0, Av1TransformSize.Size4x4, Av1BlockSize.Block4x4, 1, 0);
|
|
context.Store(Enumerable.Repeat((byte)40, 16).ToArray(), 4, 0, 0, Av1TransformSize.Size4x4, Av1BlockSize.Block4x4, 1, 1);
|
|
|
|
context.ComputeParameters(Av1TransformSize.Size4x4);
|
|
|
|
short[] expected =
|
|
[
|
|
-120, -120, -40, -40,
|
|
-120, -120, -40, -40,
|
|
40, 40, 120, 120,
|
|
40, 40, 120, 120
|
|
];
|
|
|
|
Assert.Equal(expected, GetBlock(context.Q3Buffer, 4, 4));
|
|
}
|
|
|
|
[Fact]
|
|
public void ComputeParametersPadsFrameEdgeBeforeSubtractingAverage()
|
|
{
|
|
ObuColorConfig colorConfig = new();
|
|
Av1ChromaFromLumaContext context = new(colorConfig);
|
|
byte[] input = Enumerable.Range(1, 16).Select(x => (byte)x).ToArray();
|
|
context.Store(input, 4, 0, 0, Av1TransformSize.Size4x4, Av1BlockSize.Block4x4, 0, 0);
|
|
|
|
context.ComputeParameters(Av1TransformSize.Size8x8);
|
|
|
|
short[] actual = GetBlock(context.Q3Buffer, 8, 8);
|
|
Assert.Equal(-90, actual[0]);
|
|
Assert.Equal(-66, actual[7]);
|
|
Assert.Equal(6, actual[56]);
|
|
Assert.Equal(30, actual[63]);
|
|
Assert.Equal(0, actual.Sum(x => x));
|
|
}
|
|
|
|
[Fact]
|
|
public void Predict8BitAddsScaledLumaAndClips()
|
|
{
|
|
short[] lumaQ3 = new short[32 * 32];
|
|
new short[] { -64, -32, 64, 64 }.CopyTo(lumaQ3, 0);
|
|
byte[] dcPrediction = [0, 128, 250, 255];
|
|
byte[] destination = new byte[4];
|
|
|
|
Av1PredictionDecoder.ChromaFromLumaPredict(lumaQ3, dcPrediction, 4, destination, 4, 8, Av1BitDepth.EightBit, 4, 1);
|
|
|
|
Assert.Equal(new byte[] { 0, 124, 255, 255 }, destination);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData((int)Av1BitDepth.TenBit, 1023)]
|
|
[InlineData((int)Av1BitDepth.TwelveBit, 4095)]
|
|
public void PredictHighBitDepthAddsScaledLumaAndClips(int bitDepthIndex, short maximum)
|
|
{
|
|
short[] lumaQ3 = new short[32 * 32];
|
|
new short[] { -128, -64, 64, 128 }.CopyTo(lumaQ3, 0);
|
|
short[] dcPrediction = [5, (short)(maximum / 2), (short)(maximum - 5), maximum];
|
|
short[] destination = new short[4];
|
|
|
|
Av1PredictionDecoder.ChromaFromLumaPredict(
|
|
lumaQ3,
|
|
dcPrediction,
|
|
4,
|
|
destination,
|
|
4,
|
|
16,
|
|
(Av1BitDepth)bitDepthIndex,
|
|
4,
|
|
1);
|
|
|
|
Assert.Equal(new short[] { 0, (short)((maximum / 2) - 16), maximum, maximum }, destination);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData((int)Av1Plane.U, 3)]
|
|
[InlineData((int)Av1Plane.V, 4)]
|
|
public void AlphaIndexSelectsMagnitudeForRequestedChromaPlane(int planeIndex, int expected)
|
|
{
|
|
// U occupies the high nibble and V occupies the low nibble in the packed AV1 alpha index.
|
|
const int alphaIndex = 0x23;
|
|
const int bothPositiveJointSign = 7;
|
|
|
|
int actual = Av1PredictionDecoder.ChromaFromLumaIndexToAlpha(
|
|
alphaIndex,
|
|
bothPositiveJointSign,
|
|
(Av1Plane)planeIndex);
|
|
|
|
Assert.Equal(expected, actual);
|
|
}
|
|
|
|
private static short[] GetBlock(short[] buffer, int width, int height)
|
|
{
|
|
short[] result = new short[width * height];
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
buffer.AsSpan(y * 32, width).CopyTo(result.AsSpan(y * width, width));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|