mirror of https://github.com/SixLabors/ImageSharp
18 changed files with 1094 additions and 39 deletions
@ -0,0 +1,79 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
|
|||
internal class Av1DirectionalZone1Predictor |
|||
{ |
|||
private readonly nuint blockWidth; |
|||
private readonly nuint blockHeight; |
|||
|
|||
public Av1DirectionalZone1Predictor(Size blockSize) |
|||
{ |
|||
this.blockWidth = (nuint)blockSize.Width; |
|||
this.blockHeight = (nuint)blockSize.Height; |
|||
} |
|||
|
|||
public Av1DirectionalZone1Predictor(Av1TransformSize transformSize) |
|||
{ |
|||
this.blockWidth = (nuint)transformSize.GetWidth(); |
|||
this.blockHeight = (nuint)transformSize.GetHeight(); |
|||
} |
|||
|
|||
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, bool upsampleAbove, int dx) |
|||
=> new Av1DirectionalZone1Predictor(transformSize).PredictScalar(destination, stride, above, upsampleAbove, dx); |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_dr_prediction_z1_c
|
|||
/// </summary>
|
|||
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, bool upsample, int dx) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride)); |
|||
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination)); |
|||
int upsampleAbove = upsample ? 1 : 0; |
|||
ref byte aboveRef = ref above[0]; |
|||
ref byte destinationRef = ref destination[0]; |
|||
int maxBasisX = (((int)this.blockWidth + (int)this.blockHeight) - 1) << upsampleAbove; |
|||
int fractionBitCount = 6 - upsampleAbove; |
|||
int basisIncrement = 1 << upsampleAbove; |
|||
int x = dx; |
|||
for (nuint r = 0; r < this.blockHeight; ++r) |
|||
{ |
|||
int basis = x >> fractionBitCount, shift = ((x << upsampleAbove) & 0x3F) >> 1; |
|||
|
|||
if (basis >= maxBasisX) |
|||
{ |
|||
for (nuint i = r; i < this.blockHeight; ++i) |
|||
{ |
|||
Unsafe.InitBlock(ref destinationRef, Unsafe.Add(ref aboveRef, maxBasisX), (uint)this.blockWidth); |
|||
destinationRef = ref Unsafe.Add(ref destinationRef, stride); |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
for (nuint c = 0; c < this.blockWidth; ++c) |
|||
{ |
|||
if (basis < maxBasisX) |
|||
{ |
|||
int val; |
|||
val = (Unsafe.Add(ref aboveRef, basis) * (32 - shift)) + (Unsafe.Add(ref aboveRef, basis + 1) * shift); |
|||
val = Av1Math.RoundPowerOf2(val, 5); |
|||
Unsafe.Add(ref destinationRef, c) = (byte)Av1Math.Clamp(val, 0, 255); |
|||
} |
|||
else |
|||
{ |
|||
Unsafe.Add(ref destinationRef, c) = Unsafe.Add(ref aboveRef, maxBasisX); |
|||
} |
|||
|
|||
basis += basisIncrement; |
|||
} |
|||
|
|||
x += dx; |
|||
destinationRef = ref Unsafe.Add(ref destinationRef, stride); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Security.Cryptography; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
|
|||
internal class Av1DirectionalZone2Predictor |
|||
{ |
|||
private readonly nuint blockWidth; |
|||
private readonly nuint blockHeight; |
|||
|
|||
public Av1DirectionalZone2Predictor(Size blockSize) |
|||
{ |
|||
this.blockWidth = (nuint)blockSize.Width; |
|||
this.blockHeight = (nuint)blockSize.Height; |
|||
} |
|||
|
|||
public Av1DirectionalZone2Predictor(Av1TransformSize transformSize) |
|||
{ |
|||
this.blockWidth = (nuint)transformSize.GetWidth(); |
|||
this.blockHeight = (nuint)transformSize.GetHeight(); |
|||
} |
|||
|
|||
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left, bool upsampleAbove, bool upsampleLeft, int dx, int dy) |
|||
=> new Av1DirectionalZone2Predictor(transformSize).PredictScalar(destination, stride, above, left, upsampleAbove, upsampleAbove, dx, dy); |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_dr_prediction_z1_c
|
|||
/// </summary>
|
|||
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left, bool doUpsampleAbove, bool doUpsampleLeft, int dx, int dy) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride)); |
|||
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left)); |
|||
Guard.MustBeSizedAtLeast(above, (int)this.blockWidth, nameof(above)); |
|||
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination)); |
|||
int upsampleAbove = doUpsampleAbove ? 1 : 0; |
|||
int upsampleLeft = doUpsampleLeft ? 1 : 0; |
|||
ref byte aboveRef = ref above[0]; |
|||
ref byte leftRef = ref left[0]; |
|||
ref byte destinationRef = ref destination[0]; |
|||
int minBasisX = -(1 << upsampleAbove); |
|||
int fractionBitCountX = 6 - upsampleAbove; |
|||
int fractionBitCountY = 6 - upsampleLeft; |
|||
int basisIncrementX = 1 << upsampleAbove; |
|||
int x = -dx; |
|||
for (nuint r = 0; r < this.blockHeight; ++r) |
|||
{ |
|||
int val; |
|||
int base1 = x >> fractionBitCountX; |
|||
int y = ((int)r << 6) - dy; |
|||
for (nuint c = 0; c < this.blockWidth; ++c, base1 += basisIncrementX, y -= dy) |
|||
{ |
|||
if (base1 >= minBasisX) |
|||
{ |
|||
int shift1 = ((x * (1 << upsampleAbove)) & 0x3F) >> 1; |
|||
val = (Unsafe.Add(ref aboveRef, base1) * (32 - shift1)) + (Unsafe.Add(ref aboveRef, base1 + 1) * shift1); |
|||
val = Av1Math.RoundPowerOf2(val, 5); |
|||
} |
|||
else |
|||
{ |
|||
int base2 = y >> fractionBitCountY; |
|||
Guard.MustBeGreaterThanOrEqualTo(base2, -(1 << upsampleLeft), nameof(base2)); |
|||
int shift2 = ((y * (1 << upsampleLeft)) & 0x3F) >> 1; |
|||
val = (Unsafe.Add(ref leftRef, base2) * (32 - shift2)) + (Unsafe.Add(ref leftRef, base2 + 1) * shift2); |
|||
val = Av1Math.RoundPowerOf2(val, 5); |
|||
} |
|||
|
|||
Unsafe.Add(ref destinationRef, c) = (byte)Av1Math.Clamp(val, 0, 255); |
|||
} |
|||
|
|||
x -= dx; |
|||
destinationRef = ref Unsafe.Add(ref destinationRef, stride); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
|
|||
internal class Av1DirectionalZone3Predictor |
|||
{ |
|||
private readonly nuint blockWidth; |
|||
private readonly nuint blockHeight; |
|||
|
|||
public Av1DirectionalZone3Predictor(Size blockSize) |
|||
{ |
|||
this.blockWidth = (nuint)blockSize.Width; |
|||
this.blockHeight = (nuint)blockSize.Height; |
|||
} |
|||
|
|||
public Av1DirectionalZone3Predictor(Av1TransformSize transformSize) |
|||
{ |
|||
this.blockWidth = (nuint)transformSize.GetWidth(); |
|||
this.blockHeight = (nuint)transformSize.GetHeight(); |
|||
} |
|||
|
|||
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> left, bool upsampleAbove, int dx, int dy) |
|||
=> new Av1DirectionalZone3Predictor(transformSize).PredictScalar(destination, stride, left, upsampleAbove, dx, dy); |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_dr_prediction_z3_c
|
|||
/// </summary>
|
|||
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> left, bool upsample, int dx, int dy) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride)); |
|||
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left)); |
|||
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination)); |
|||
int upsampleLeft = upsample ? 1 : 0; |
|||
ref byte leftRef = ref left[0]; |
|||
ref byte destinationRef = ref destination[0]; |
|||
Guard.IsTrue(dx == 1, nameof(dx), "Dx expected to be always equal to 1 for directional Zone 3 prediction."); |
|||
Guard.MustBeGreaterThan(dy, 0, nameof(dy)); |
|||
|
|||
int maxBasisY = ((int)this.blockWidth + (int)this.blockHeight - 1) << upsampleLeft; |
|||
int fractionBitCount = 6 - upsampleLeft; |
|||
int basisIncrement = 1 << upsampleLeft; |
|||
int y = dy; |
|||
for (nuint c = 0; c < this.blockWidth; ++c) |
|||
{ |
|||
int basis = y >> fractionBitCount; |
|||
int shift = ((y << upsampleLeft) & 0x3F) >> 1; |
|||
|
|||
for (nuint r = 0; r < this.blockHeight; ++r) |
|||
{ |
|||
if (basis < maxBasisY) |
|||
{ |
|||
int val; |
|||
val = (Unsafe.Add(ref leftRef, basis) * (32 - shift)) + (Unsafe.Add(ref leftRef, basis + 1) * shift); |
|||
val = Av1Math.RoundPowerOf2(val, 5); |
|||
Unsafe.Add(ref destinationRef, (r * stride) + c) = (byte)Av1Math.Clamp(val, 0, 255); |
|||
} |
|||
else |
|||
{ |
|||
for (; r < this.blockHeight; ++r) |
|||
{ |
|||
Unsafe.Add(ref destinationRef, (r * stride) + c) = left[maxBasisY]; |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
basis += basisIncrement; |
|||
} |
|||
|
|||
y += dy; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
|
|||
internal class Av1HorizontalPredictor : IAv1Predictor |
|||
{ |
|||
private readonly nuint blockWidth; |
|||
private readonly nuint blockHeight; |
|||
|
|||
public Av1HorizontalPredictor(Size blockSize) |
|||
{ |
|||
this.blockWidth = (nuint)blockSize.Width; |
|||
this.blockHeight = (nuint)blockSize.Height; |
|||
} |
|||
|
|||
public Av1HorizontalPredictor(Av1TransformSize transformSize) |
|||
{ |
|||
this.blockWidth = (nuint)transformSize.GetWidth(); |
|||
this.blockHeight = (nuint)transformSize.GetHeight(); |
|||
} |
|||
|
|||
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
=> new Av1HorizontalPredictor(transformSize).PredictScalar(destination, stride, above, left); |
|||
|
|||
/// <summary>
|
|||
/// SVT: highbd_h_predictor
|
|||
/// </summary>
|
|||
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride)); |
|||
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left)); |
|||
Guard.MustBeSizedAtLeast(above, (int)this.blockWidth, nameof(above)); |
|||
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination)); |
|||
ref byte leftRef = ref left[0]; |
|||
ref byte destinationRef = ref destination[0]; |
|||
uint width = (uint)this.blockWidth; |
|||
for (nuint r = 0; r < this.blockHeight; ++r) |
|||
{ |
|||
Unsafe.InitBlock(ref destinationRef, Unsafe.Add(ref leftRef, r), width); |
|||
destinationRef = ref Unsafe.Add(ref destinationRef, stride); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
|
|||
internal class Av1PaethPredictor : IAv1Predictor |
|||
{ |
|||
private readonly uint blockWidth; |
|||
private readonly uint blockHeight; |
|||
|
|||
public Av1PaethPredictor(Size blockSize) |
|||
{ |
|||
this.blockWidth = (uint)blockSize.Width; |
|||
this.blockHeight = (uint)blockSize.Height; |
|||
} |
|||
|
|||
public Av1PaethPredictor(Av1TransformSize transformSize) |
|||
{ |
|||
this.blockWidth = (uint)transformSize.GetWidth(); |
|||
this.blockHeight = (uint)transformSize.GetHeight(); |
|||
} |
|||
|
|||
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
=> new Av1DcPredictor(transformSize).PredictScalar(destination, stride, above, left); |
|||
|
|||
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride)); |
|||
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left)); |
|||
Guard.MustBeSizedAtLeast(above, (int)this.blockWidth, nameof(above)); |
|||
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination)); |
|||
ref byte leftRef = ref left[0]; |
|||
ref byte aboveRef = ref above[0]; |
|||
int yTopLeft = above[-1]; |
|||
ref byte destinationRef = ref destination[0]; |
|||
for (nuint r = 0; r < this.blockHeight; r++) |
|||
{ |
|||
for (nuint c = 0; c < this.blockWidth; c++) |
|||
{ |
|||
destinationRef = PredictSingle(Unsafe.Add(ref leftRef, r), Unsafe.Add(ref aboveRef, c), yTopLeft); |
|||
destinationRef = ref Unsafe.Add(ref destinationRef, stride); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private static byte PredictSingle(byte left, byte top, int topLeft) |
|||
{ |
|||
int basis = top + left - topLeft; |
|||
int pLeft = Av1Math.AbsoluteDifference(basis, left); |
|||
int pTop = Av1Math.AbsoluteDifference(basis, top); |
|||
int pTopLeft = Av1Math.AbsoluteDifference(basis, topLeft); |
|||
|
|||
// Return nearest to base of left, top and top_left.
|
|||
return (byte)((pLeft <= pTop && pLeft <= pTopLeft) ? left : (pTop <= pTopLeft) ? top : topLeft); |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
|
|||
internal class Av1SmoothHorizontalPredictor : IAv1Predictor |
|||
{ |
|||
private readonly nuint blockWidth; |
|||
private readonly nuint blockHeight; |
|||
|
|||
public Av1SmoothHorizontalPredictor(Size blockSize) |
|||
{ |
|||
this.blockWidth = (nuint)blockSize.Width; |
|||
this.blockHeight = (nuint)blockSize.Height; |
|||
} |
|||
|
|||
public Av1SmoothHorizontalPredictor(Av1TransformSize transformSize) |
|||
{ |
|||
this.blockWidth = (nuint)transformSize.GetWidth(); |
|||
this.blockHeight = (nuint)transformSize.GetHeight(); |
|||
} |
|||
|
|||
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
=> new Av1SmoothHorizontalPredictor(transformSize).PredictScalar(destination, stride, above, left); |
|||
|
|||
/// <summary>
|
|||
/// SVT: highbd_smooth_h_predictor
|
|||
/// </summary>
|
|||
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride)); |
|||
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left)); |
|||
Guard.MustBeSizedAtLeast(above, (int)this.blockWidth, nameof(above)); |
|||
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination)); |
|||
ref byte leftRef = ref left[0]; |
|||
ref byte aboveRef = ref above[0]; |
|||
ref byte destinationRef = ref destination[0]; |
|||
int rightPrediction = Unsafe.Add(ref aboveRef, this.blockWidth - 1); // estimated by top-right pixel
|
|||
ref int weights = ref Av1SmoothPredictor.Weights[(int)this.blockWidth]; |
|||
|
|||
// scale = 2 * 2^sm_weight_log2_scale
|
|||
int log2Scale = 1 + Av1SmoothPredictor.WeightLog2Scale; |
|||
int scale = 1 << Av1SmoothPredictor.WeightLog2Scale; |
|||
|
|||
// sm_weights_sanity_checks(sm_weights_w, sm_weights_h, scale, log2_scale + 2);
|
|||
for (nuint r = 0; r < this.blockHeight; ++r) |
|||
{ |
|||
for (nuint c = 0; c < this.blockWidth; ++c) |
|||
{ |
|||
int columnWeight = Unsafe.Add(ref weights, c); |
|||
Guard.MustBeGreaterThanOrEqualTo(scale, columnWeight, nameof(scale)); |
|||
int thisPredition = Unsafe.Add(ref leftRef, r) * columnWeight; |
|||
thisPredition += rightPrediction * (scale - columnWeight); |
|||
Unsafe.Add(ref destinationRef, c) = (byte)Av1Math.DivideRound(thisPredition, log2Scale); |
|||
} |
|||
|
|||
destinationRef = ref Unsafe.Add(ref destinationRef, stride); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
|
|||
internal class Av1SmoothPredictor : IAv1Predictor |
|||
{ |
|||
// Weights are quadratic from '1' to '1 / BlockSize', scaled by
|
|||
// 2^sm_weight_log2_scale.
|
|||
internal static readonly int WeightLog2Scale = 8; |
|||
|
|||
internal static readonly int[] Weights = [ |
|||
|
|||
// Unused, because we always offset by bs, which is at least 2.
|
|||
0, 0, |
|||
|
|||
// bs = 2
|
|||
255, 128, |
|||
|
|||
// bs = 4
|
|||
255, 149, 85, 64, |
|||
|
|||
// bs = 8
|
|||
255, 197, 146, 105, 73, 50, 37, 32, |
|||
|
|||
// bs = 16
|
|||
255, 225, 196, 170, 145, 123, 102, 84, 68, 54, 43, 33, 26, 20, 17, 16, |
|||
|
|||
// bs = 32
|
|||
255, 240, 225, 210, 196, 182, 169, 157, 145, 133, 122, 111, 101, 92, 83, 74, |
|||
66, 59, 52, 45, 39, 34, 29, 25, 21, 17, 14, 12, 10, 9, 8, 8, |
|||
|
|||
// bs = 64
|
|||
255, 248, 240, 233, 225, 218, 210, 203, 196, 189, 182, 176, 169, 163, 156, |
|||
150, 144, 138, 133, 127, 121, 116, 111, 106, 101, 96, 91, 86, 82, 77, 73, 69, |
|||
65, 61, 57, 54, 50, 47, 44, 41, 38, 35, 32, 29, 27, 25, 22, 20, 18, 16, 15, |
|||
13, 12, 10, 9, 8, 7, 6, 6, 5, 5, 4, 4, 4, |
|||
]; |
|||
|
|||
private readonly nuint blockWidth; |
|||
private readonly nuint blockHeight; |
|||
|
|||
public Av1SmoothPredictor(Size blockSize) |
|||
{ |
|||
this.blockWidth = (nuint)blockSize.Width; |
|||
this.blockHeight = (nuint)blockSize.Height; |
|||
} |
|||
|
|||
public Av1SmoothPredictor(Av1TransformSize transformSize) |
|||
{ |
|||
this.blockWidth = (nuint)transformSize.GetWidth(); |
|||
this.blockHeight = (nuint)transformSize.GetHeight(); |
|||
} |
|||
|
|||
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
=> new Av1SmoothPredictor(transformSize).PredictScalar(destination, stride, above, left); |
|||
|
|||
/// <summary>
|
|||
/// SVT: highbd_smooth_predictor
|
|||
/// </summary>
|
|||
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride)); |
|||
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left)); |
|||
Guard.MustBeSizedAtLeast(above, (int)this.blockWidth, nameof(above)); |
|||
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination)); |
|||
ref byte leftRef = ref left[0]; |
|||
ref byte aboveRef = ref above[0]; |
|||
ref byte destinationRef = ref destination[0]; |
|||
int belowPrediction = Unsafe.Add(ref leftRef, this.blockHeight - 1); // estimated by bottom-left pixel
|
|||
int rightPrediction = Unsafe.Add(ref aboveRef, this.blockWidth - 1); // estimated by top-right pixel
|
|||
ref int heightWeights = ref Weights[(int)this.blockWidth]; |
|||
ref int widthWeights = ref Weights[(int)this.blockHeight]; |
|||
|
|||
// scale = 2 * 2^sm_weight_log2_scale
|
|||
int log2Scale = 1 + WeightLog2Scale; |
|||
int scale = 1 << WeightLog2Scale; |
|||
|
|||
// sm_weights_sanity_checks(sm_weights_w, sm_weights_h, scale, log2_scale + 2);
|
|||
for (nuint r = 0; r < this.blockHeight; ++r) |
|||
{ |
|||
int rowWeight = Unsafe.Add(ref heightWeights, r); |
|||
Guard.MustBeGreaterThanOrEqualTo(scale, rowWeight, nameof(scale)); |
|||
for (nuint c = 0; c < this.blockWidth; ++c) |
|||
{ |
|||
int columnWeight = Unsafe.Add(ref widthWeights, c); |
|||
Guard.MustBeGreaterThanOrEqualTo(scale, columnWeight, nameof(scale)); |
|||
int thisPredition = Unsafe.Add(ref aboveRef, c) * rowWeight; |
|||
thisPredition += belowPrediction * (scale - rowWeight); |
|||
thisPredition += Unsafe.Add(ref leftRef, r) * columnWeight; |
|||
thisPredition += rightPrediction * (scale - columnWeight); |
|||
Unsafe.Add(ref destinationRef, c) = (byte)Av1Math.DivideRound(thisPredition, log2Scale); |
|||
} |
|||
|
|||
destinationRef = ref Unsafe.Add(ref destinationRef, stride); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,62 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
|
|||
internal class Av1SmoothVerticalPredictor : IAv1Predictor |
|||
{ |
|||
private readonly nuint blockWidth; |
|||
private readonly nuint blockHeight; |
|||
|
|||
public Av1SmoothVerticalPredictor(Size blockSize) |
|||
{ |
|||
this.blockWidth = (nuint)blockSize.Width; |
|||
this.blockHeight = (nuint)blockSize.Height; |
|||
} |
|||
|
|||
public Av1SmoothVerticalPredictor(Av1TransformSize transformSize) |
|||
{ |
|||
this.blockWidth = (nuint)transformSize.GetWidth(); |
|||
this.blockHeight = (nuint)transformSize.GetHeight(); |
|||
} |
|||
|
|||
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
=> new Av1SmoothVerticalPredictor(transformSize).PredictScalar(destination, stride, above, left); |
|||
|
|||
/// <summary>
|
|||
/// SVT: highbd_smooth_v_predictor
|
|||
/// </summary>
|
|||
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride)); |
|||
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left)); |
|||
Guard.MustBeSizedAtLeast(above, (int)this.blockWidth, nameof(above)); |
|||
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination)); |
|||
ref byte leftRef = ref left[0]; |
|||
ref byte aboveRef = ref above[0]; |
|||
ref byte destinationRef = ref destination[0]; |
|||
int belowPrediction = Unsafe.Add(ref leftRef, this.blockHeight - 1); // estimated by bottom-left pixel
|
|||
ref int weights = ref Av1SmoothPredictor.Weights[(int)this.blockHeight]; |
|||
|
|||
// scale = 2 * 2^sm_weight_log2_scale
|
|||
int log2Scale = 1 + Av1SmoothPredictor.WeightLog2Scale; |
|||
int scale = 1 << Av1SmoothPredictor.WeightLog2Scale; |
|||
|
|||
// sm_weights_sanity_checks(sm_weights_w, sm_weights_h, scale, log2_scale + 2);
|
|||
for (nuint r = 0; r < this.blockHeight; ++r) |
|||
{ |
|||
int rowWeight = Unsafe.Add(ref weights, r); |
|||
for (nuint c = 0; c < this.blockWidth; ++c) |
|||
{ |
|||
int thisPredition = Unsafe.Add(ref aboveRef, c) * rowWeight; |
|||
thisPredition += belowPrediction * (scale - rowWeight); |
|||
Unsafe.Add(ref destinationRef, c) = (byte)Av1Math.DivideRound(thisPredition, log2Scale); |
|||
} |
|||
|
|||
destinationRef = ref Unsafe.Add(ref destinationRef, stride); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
|||
|
|||
internal class Av1VerticalPredictor : IAv1Predictor |
|||
{ |
|||
private readonly nuint blockWidth; |
|||
private readonly nuint blockHeight; |
|||
|
|||
public Av1VerticalPredictor(Size blockSize) |
|||
{ |
|||
this.blockWidth = (nuint)blockSize.Width; |
|||
this.blockHeight = (nuint)blockSize.Height; |
|||
} |
|||
|
|||
public Av1VerticalPredictor(Av1TransformSize transformSize) |
|||
{ |
|||
this.blockWidth = (nuint)transformSize.GetWidth(); |
|||
this.blockHeight = (nuint)transformSize.GetHeight(); |
|||
} |
|||
|
|||
public static void PredictScalar(Av1TransformSize transformSize, Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
=> new Av1VerticalPredictor(transformSize).PredictScalar(destination, stride, above, left); |
|||
|
|||
/// <summary>
|
|||
/// SVT: highbd_v_predictor
|
|||
/// </summary>
|
|||
public void PredictScalar(Span<byte> destination, nuint stride, Span<byte> above, Span<byte> left) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(stride, this.blockWidth, nameof(stride)); |
|||
Guard.MustBeSizedAtLeast(left, (int)this.blockHeight, nameof(left)); |
|||
Guard.MustBeSizedAtLeast(above, (int)this.blockWidth, nameof(above)); |
|||
Guard.MustBeSizedAtLeast(destination, (int)this.blockHeight * (int)stride, nameof(destination)); |
|||
ref byte aboveRef = ref above[0]; |
|||
ref byte destinationRef = ref destination[0]; |
|||
uint width = (uint)this.blockWidth; |
|||
for (nuint r = 0; r < this.blockHeight; ++r) |
|||
{ |
|||
Unsafe.CopyBlock(ref destinationRef, ref aboveRef, width); |
|||
destinationRef = ref Unsafe.Add(ref destinationRef, stride); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,104 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Globalization; |
|||
using System.Security.Cryptography; |
|||
using System.Text; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; |
|||
|
|||
internal class Av1IntraPredictionMemory |
|||
{ |
|||
public const int Padding = 16; |
|||
private const int TopPadding = 7; |
|||
private const int MaxBlockSizeTimes2 = 1 << Av1Constants.MaxSuperBlockSizeLog2; |
|||
private const int TotalPixelCount = 4096; |
|||
private readonly byte[] destination = new byte[TotalPixelCount]; |
|||
private readonly byte[] referenceSource = new byte[TotalPixelCount]; |
|||
private readonly byte[] leftMemory = new byte[MaxBlockSizeTimes2 + Padding]; |
|||
private readonly byte[] topMemory = new byte[MaxBlockSizeTimes2 + Padding + TopPadding]; |
|||
private readonly int bitDepth; |
|||
|
|||
public Av1IntraPredictionMemory(int bitDepth) => this.bitDepth = bitDepth; |
|||
|
|||
public Span<byte> Destination => this.destination; |
|||
|
|||
public Span<byte> Left => this.leftMemory; |
|||
|
|||
public Span<byte> Top => this.topMemory; |
|||
|
|||
/// <summary>
|
|||
/// Sets referenceSource, left and top to <paramref name="value"/>.
|
|||
/// </summary>
|
|||
public void Set(byte value) |
|||
{ |
|||
for (int r = 0; r < this.referenceSource.Length; r++) |
|||
{ |
|||
this.referenceSource[r] = value; |
|||
} |
|||
|
|||
// Upsampling in the directional predictors extends left/top[-1] to [-2].
|
|||
for (int i = Padding - 2; i < Padding + MaxBlockSizeTimes2; ++i) |
|||
{ |
|||
this.leftMemory[i] = this.topMemory[i] = value; |
|||
} |
|||
} |
|||
|
|||
public void Scramble(Random rnd) |
|||
{ |
|||
for (int i = 0; i < this.referenceSource.Length; i++) |
|||
{ |
|||
this.referenceSource[i] = this.GetRandomValue(rnd); |
|||
} |
|||
|
|||
for (int i = Padding; i < (MaxBlockSizeTimes2 >> 1) + Padding; ++i) |
|||
{ |
|||
this.leftMemory[i] = this.GetRandomValue(rnd); |
|||
} |
|||
|
|||
for (int i = Padding - 1; i < (MaxBlockSizeTimes2 >> 1) + Padding; ++i) |
|||
{ |
|||
this.topMemory[i] = this.GetRandomValue(rnd); |
|||
} |
|||
|
|||
// Some directional predictors require top-right, bottom-left.
|
|||
for (int i = MaxBlockSizeTimes2 >> 1; i < MaxBlockSizeTimes2; ++i) |
|||
{ |
|||
this.leftMemory[Padding + i] = this.GetRandomValue(rnd); |
|||
this.topMemory[Padding + i] = this.GetRandomValue(rnd); |
|||
} |
|||
|
|||
// TODO(jzern): reorder this and regenerate the digests after switching
|
|||
// random number generators.
|
|||
// Upsampling in the directional predictors extends left/top[-1] to [-2].
|
|||
this.leftMemory[Padding - 1] = this.GetRandomValue(rnd); |
|||
this.leftMemory[Padding - 2] = this.GetRandomValue(rnd); |
|||
this.topMemory[Padding - 2] = this.GetRandomValue(rnd); |
|||
this.leftMemory.AsSpan(0, Padding - 2).Clear(); |
|||
this.topMemory.AsSpan(0, Padding - 2).Clear(); |
|||
this.topMemory.AsSpan(MaxBlockSizeTimes2 + Padding, TopPadding).Clear(); |
|||
} |
|||
|
|||
public void CopySourceToDestination() => this.referenceSource.CopyTo(this.destination.AsSpan()); |
|||
|
|||
/// <summary>
|
|||
/// Return a hash string of a block of bytes.
|
|||
/// </summary>
|
|||
/// <remarks>This test design is used extensively in 'libgav1' tests, some of which are ported here.</remarks>
|
|||
public static string GetDigest(byte[] input) |
|||
{ |
|||
byte[] hash = MD5.HashData(input); |
|||
StringBuilder sb = new(); |
|||
for (int i = 0; i < hash.Length; i++) |
|||
{ |
|||
sb.Append(hash[i].ToString("X2", CultureInfo.InvariantCulture)); |
|||
} |
|||
|
|||
return sb.ToString(); |
|||
} |
|||
|
|||
public string GetDestinationDigest() => GetDigest(this.destination); |
|||
|
|||
private byte GetRandomValue(Random random) => (byte)(random.Next(1 << 16) & ((1 << this.bitDepth) - 1)); |
|||
} |
|||
Loading…
Reference in new issue