mirror of https://github.com/SixLabors/ImageSharp
7 changed files with 315 additions and 73 deletions
@ -0,0 +1,134 @@ |
|||
// 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); |
|||
} |
|||
|
|||
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; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue