Browse Source

Introduce DC predictors

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
d748a0d804
  1. 28
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcFillPredictor.cs
  2. 34
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcLeftPredictor.cs
  3. 40
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcPredictor.cs
  4. 34
      src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcTopPredictor.cs
  5. 19
      src/ImageSharp/Formats/Heif/Av1/Prediction/IAv1Predictor.cs
  6. 123
      tests/ImageSharp.Tests/Formats/Heif/Av1/PredictorTests.cs
  7. 2
      tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTests.cs

28
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcFillPredictor.cs

@ -0,0 +1,28 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
internal class Av1DcFillPredictor : IAv1Predictor
{
private readonly uint blockWidth;
private readonly uint blockHeight;
public Av1DcFillPredictor(Size blockSize)
{
this.blockWidth = (uint)blockSize.Width;
this.blockHeight = (uint)blockSize.Height;
}
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left)
{
const byte expectedDc = 0x80;
for (uint r = 0; r < this.blockHeight; r++)
{
Unsafe.InitBlock(ref destination, expectedDc, this.blockWidth);
destination = ref Unsafe.Add(ref destination, stride);
}
}
}

34
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcLeftPredictor.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
internal class Av1DcLeftPredictor : IAv1Predictor
{
private readonly uint blockWidth;
private readonly uint blockHeight;
public Av1DcLeftPredictor(Size blockSize)
{
this.blockWidth = (uint)blockSize.Width;
this.blockHeight = (uint)blockSize.Height;
}
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left)
{
int sum = 0;
for (uint i = 0; i < this.blockHeight; i++)
{
sum += Unsafe.Add(ref left, i);
}
byte expectedDc = (byte)((sum + (this.blockHeight >> 1)) / this.blockHeight);
for (uint r = 0; r < this.blockHeight; r++)
{
Unsafe.InitBlock(ref destination, expectedDc, this.blockWidth);
destination = ref Unsafe.Add(ref destination, stride);
}
}
}

40
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcPredictor.cs

@ -0,0 +1,40 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
internal class Av1DcPredictor : IAv1Predictor
{
private readonly uint blockWidth;
private readonly uint blockHeight;
public Av1DcPredictor(Size blockSize)
{
this.blockWidth = (uint)blockSize.Width;
this.blockHeight = (uint)blockSize.Height;
}
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left)
{
int sum = 0;
uint count = this.blockWidth + this.blockHeight;
for (uint i = 0; i < this.blockWidth; i++)
{
sum += Unsafe.Add(ref above, i);
}
for (uint i = 0; i < this.blockHeight; i++)
{
sum += Unsafe.Add(ref left, i);
}
byte expectedDc = (byte)((sum + (count >> 1)) / count);
for (uint r = 0; r < this.blockHeight; r++)
{
Unsafe.InitBlock(ref destination, expectedDc, this.blockWidth);
destination = ref Unsafe.Add(ref destination, stride);
}
}
}

34
src/ImageSharp/Formats/Heif/Av1/Prediction/Av1DcTopPredictor.cs

@ -0,0 +1,34 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
internal class Av1DcTopPredictor : IAv1Predictor
{
private readonly uint blockWidth;
private readonly uint blockHeight;
public Av1DcTopPredictor(Size blockSize)
{
this.blockWidth = (uint)blockSize.Width;
this.blockHeight = (uint)blockSize.Height;
}
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left)
{
int sum = 0;
for (uint i = 0; i < this.blockWidth; i++)
{
sum += Unsafe.Add(ref above, i);
}
byte expectedDc = (byte)((sum + (this.blockWidth >> 1)) / this.blockWidth);
for (uint r = 0; r < this.blockHeight; r++)
{
Unsafe.InitBlock(ref destination, expectedDc, this.blockWidth);
destination = ref Unsafe.Add(ref destination, stride);
}
}
}

19
src/ImageSharp/Formats/Heif/Av1/Prediction/IAv1Predictor.cs

@ -0,0 +1,19 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
/// <summary>
/// Interface for predictor implementations.
/// </summary>
internal interface IAv1Predictor
{
/// <summary>
/// Predict using scalar logic within the 8-bit pipeline.
/// </summary>
/// <param name="destination">The destination to write to.</param>
/// <param name="stride">The stride of the destination buffer.</param>
/// <param name="above">Pointer to the first element of the block above.</param>
/// <param name="left">Pointer to the first element of the block to the left.</param>
public void PredictScalar(ref byte destination, nuint stride, ref byte above, ref byte left);
}

123
tests/ImageSharp.Tests/Formats/Heif/Av1/PredictorTests.cs

@ -0,0 +1,123 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using SixLabors.ImageSharp.Formats.Heif.Av1.Prediction;
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
[Trait("Format", "Avif")]
public class PredictorTests
{
[Theory]
[MemberData(nameof(GetTransformSizes))]
public void VerifyDcFill(int width, int height)
{
// Assign
byte[] destination = new byte[width * height];
byte[] left = new byte[1];
byte[] above = new byte[1];
byte expected = 0x80;
// Act
Av1DcFillPredictor predictor = new(new Size(width, height));
predictor.PredictScalar(ref destination[0], (nuint)width, ref above[0], ref left[0]);
// Assert
Assert.All(destination, (b) => AssertValue(expected, b));
}
[Theory]
[MemberData(nameof(GetTransformSizes))]
public void VerifyDc(int width, int height)
{
// Assign
byte[] destination = new byte[width * height];
byte[] left = new byte[width * height];
byte[] above = new byte[width * height];
Array.Fill(left, (byte)5);
Array.Fill(above, (byte)28);
int count = width + height;
int sum = Sum(left, height) + Sum(above, width);
byte expected = (byte)((sum + (count >> 1)) / count);
// Act
Av1DcPredictor predictor = new(new Size(width, height));
predictor.PredictScalar(ref destination[0], (nuint)width, ref above[0], ref left[0]);
// Assert
Assert.Equal((5 * height) + (28 * width), sum);
Assert.All(destination, (b) => AssertValue(expected, b));
}
[Theory]
[MemberData(nameof(GetTransformSizes))]
public void VerifyDcLeft(int width, int height)
{
// Assign
byte[] destination = new byte[width * height];
byte[] left = new byte[width * height];
byte[] above = new byte[width * height];
Array.Fill(left, (byte)5);
Array.Fill(above, (byte)28);
byte expected = left[0];
// Act
Av1DcLeftPredictor predictor = new(new Size(width, height));
predictor.PredictScalar(ref destination[0], (nuint)width, ref above[0], ref left[0]);
// Assert
Assert.All(destination, (b) => AssertValue(expected, b));
}
[Theory]
[MemberData(nameof(GetTransformSizes))]
public void VerifyDcTop(int width, int height)
{
// Assign
byte[] destination = new byte[width * height];
byte[] left = new byte[width * height];
byte[] above = new byte[width * height];
Array.Fill(left, (byte)5);
Array.Fill(above, (byte)28);
byte expected = above[0];
// Act
Av1DcTopPredictor predictor = new(new Size(width, height));
predictor.PredictScalar(ref destination[0], (nuint)width, ref above[0], ref left[0]);
// Assert
Assert.All(destination, (b) => AssertValue(expected, b));
}
private static void AssertValue(byte expected, byte actual)
{
Assert.NotEqual(0, actual);
Assert.Equal(expected, actual);
}
private static int Sum(Span<byte> values, int length)
{
int sum = 0;
for (int i = 0; i < length; i++)
{
sum += values[i];
}
return sum;
}
public static TheoryData<int, int> GetTransformSizes()
{
TheoryData<int, int> combinations = [];
for (int s = 0; s < (int)Av1TransformSize.AllSizes; s++)
{
Av1TransformSize size = (Av1TransformSize)s;
int width = size.GetWidth();
int height = size.GetHeight();
combinations.Add(width, height);
}
return combinations;
}
}

2
tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTest.cs → tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTests.cs

@ -9,7 +9,7 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
[Trait("Format", "Avif")]
public class SymbolTest
public class SymbolTests
{
[Fact]
public void ReadRandomLiteral()
Loading…
Cancel
Save