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