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