mirror of https://github.com/SixLabors/ImageSharp
5 changed files with 244 additions and 8 deletions
@ -0,0 +1,150 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; |
||||
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Prediction; |
||||
|
|
||||
|
internal static class Av1FilterIntraPredictor |
||||
|
{ |
||||
|
private const int BufferStride = 33; |
||||
|
private const int BufferLength = BufferStride * BufferStride; |
||||
|
private const int TapsPerPixel = 7; |
||||
|
private const int PixelsPerGroup = 8; |
||||
|
private const int TapsPerMode = TapsPerPixel * PixelsPerGroup; |
||||
|
|
||||
|
// AV1 7.11.2.3 defines five sets of eight filters over the same seven
|
||||
|
// already-reconstructed neighbors. The omitted eighth libaom tap is zero.
|
||||
|
private static readonly sbyte[] Taps = |
||||
|
[ |
||||
|
|
||||
|
// DC
|
||||
|
-6, 10, 0, 0, 0, 12, 0, |
||||
|
-5, 2, 10, 0, 0, 9, 0, |
||||
|
-3, 1, 1, 10, 0, 7, 0, |
||||
|
-3, 1, 1, 2, 10, 5, 0, |
||||
|
-4, 6, 0, 0, 0, 2, 12, |
||||
|
-3, 2, 6, 0, 0, 2, 9, |
||||
|
-3, 2, 2, 6, 0, 2, 7, |
||||
|
-3, 1, 2, 2, 6, 3, 5, |
||||
|
|
||||
|
// Vertical
|
||||
|
-10, 16, 0, 0, 0, 10, 0, |
||||
|
-6, 0, 16, 0, 0, 6, 0, |
||||
|
-4, 0, 0, 16, 0, 4, 0, |
||||
|
-2, 0, 0, 0, 16, 2, 0, |
||||
|
-10, 16, 0, 0, 0, 0, 10, |
||||
|
-6, 0, 16, 0, 0, 0, 6, |
||||
|
-4, 0, 0, 16, 0, 0, 4, |
||||
|
-2, 0, 0, 0, 16, 0, 2, |
||||
|
|
||||
|
// Horizontal
|
||||
|
-8, 8, 0, 0, 0, 16, 0, |
||||
|
-8, 0, 8, 0, 0, 16, 0, |
||||
|
-8, 0, 0, 8, 0, 16, 0, |
||||
|
-8, 0, 0, 0, 8, 16, 0, |
||||
|
-4, 4, 0, 0, 0, 0, 16, |
||||
|
-4, 0, 4, 0, 0, 0, 16, |
||||
|
-4, 0, 0, 4, 0, 0, 16, |
||||
|
-4, 0, 0, 0, 4, 0, 16, |
||||
|
|
||||
|
// Directional 157 degrees
|
||||
|
-2, 8, 0, 0, 0, 10, 0, |
||||
|
-1, 3, 8, 0, 0, 6, 0, |
||||
|
-1, 2, 3, 8, 0, 4, 0, |
||||
|
0, 1, 2, 3, 8, 2, 0, |
||||
|
-1, 4, 0, 0, 0, 3, 10, |
||||
|
-1, 3, 4, 0, 0, 4, 6, |
||||
|
-1, 2, 3, 4, 0, 4, 4, |
||||
|
-1, 2, 2, 3, 4, 3, 3, |
||||
|
|
||||
|
// Paeth
|
||||
|
-12, 14, 0, 0, 0, 14, 0, |
||||
|
-10, 0, 14, 0, 0, 12, 0, |
||||
|
-9, 0, 0, 14, 0, 11, 0, |
||||
|
-8, 0, 0, 0, 14, 10, 0, |
||||
|
-10, 12, 0, 0, 0, 0, 14, |
||||
|
-9, 1, 12, 0, 0, 0, 12, |
||||
|
-8, 0, 0, 12, 0, 1, 11, |
||||
|
-7, 0, 0, 1, 12, 1, 9, |
||||
|
]; |
||||
|
|
||||
|
internal static void Predict( |
||||
|
Span<byte> destination, |
||||
|
nuint destinationStride, |
||||
|
Av1TransformSize transformSize, |
||||
|
Span<byte> above, |
||||
|
Span<byte> left, |
||||
|
Av1FilterIntraMode mode) |
||||
|
{ |
||||
|
int width = transformSize.GetWidth(); |
||||
|
int height = transformSize.GetHeight(); |
||||
|
DebugGuard.MustBeLessThanOrEqualTo(width, 32, nameof(width)); |
||||
|
DebugGuard.MustBeLessThanOrEqualTo(height, 32, nameof(height)); |
||||
|
Guard.MustBeGreaterThanOrEqualTo(destinationStride, (nuint)width, nameof(destinationStride)); |
||||
|
Guard.MustBeSizedAtLeast(destination, (int)destinationStride * height, nameof(destination)); |
||||
|
Guard.MustBeSizedAtLeast(above, width, nameof(above)); |
||||
|
Guard.MustBeSizedAtLeast(left, height, nameof(left)); |
||||
|
|
||||
|
Span<byte> buffer = stackalloc byte[BufferLength]; |
||||
|
ref byte bufferRef = ref buffer[0]; |
||||
|
ref byte aboveRef = ref above[0]; |
||||
|
ref byte leftRef = ref left[0]; |
||||
|
|
||||
|
// Row zero includes the top-left sample followed by the top neighbors.
|
||||
|
bufferRef = Unsafe.Subtract(ref aboveRef, 1); |
||||
|
above[..width].CopyTo(buffer[1..]); |
||||
|
for (int row = 0; row < height; row++) |
||||
|
{ |
||||
|
Unsafe.Add(ref bufferRef, (row + 1) * BufferStride) = Unsafe.Add(ref leftRef, row); |
||||
|
} |
||||
|
|
||||
|
int modeOffset = (int)mode * TapsPerMode; |
||||
|
ref sbyte tapsRef = ref Taps[modeOffset]; |
||||
|
for (int row = 1; row <= height; row += 2) |
||||
|
{ |
||||
|
for (int column = 1; column <= width; column += 4) |
||||
|
{ |
||||
|
int sourceOffset = ((row - 1) * BufferStride) + column - 1; |
||||
|
int p0 = Unsafe.Add(ref bufferRef, sourceOffset); |
||||
|
int p1 = Unsafe.Add(ref bufferRef, sourceOffset + 1); |
||||
|
int p2 = Unsafe.Add(ref bufferRef, sourceOffset + 2); |
||||
|
int p3 = Unsafe.Add(ref bufferRef, sourceOffset + 3); |
||||
|
int p4 = Unsafe.Add(ref bufferRef, sourceOffset + 4); |
||||
|
int p5 = Unsafe.Add(ref bufferRef, sourceOffset + BufferStride); |
||||
|
int p6 = Unsafe.Add(ref bufferRef, sourceOffset + (2 * BufferStride)); |
||||
|
|
||||
|
for (int pixel = 0; pixel < PixelsPerGroup; pixel++) |
||||
|
{ |
||||
|
int tapOffset = pixel * TapsPerPixel; |
||||
|
int prediction = |
||||
|
(Unsafe.Add(ref tapsRef, tapOffset) * p0) |
||||
|
+ (Unsafe.Add(ref tapsRef, tapOffset + 1) * p1) |
||||
|
+ (Unsafe.Add(ref tapsRef, tapOffset + 2) * p2) |
||||
|
+ (Unsafe.Add(ref tapsRef, tapOffset + 3) * p3) |
||||
|
+ (Unsafe.Add(ref tapsRef, tapOffset + 4) * p4) |
||||
|
+ (Unsafe.Add(ref tapsRef, tapOffset + 5) * p5) |
||||
|
+ (Unsafe.Add(ref tapsRef, tapOffset + 6) * p6); |
||||
|
|
||||
|
int rowOffset = pixel >> 2; |
||||
|
int columnOffset = pixel & 3; |
||||
|
int destinationOffset = ((row + rowOffset) * BufferStride) + column + columnOffset; |
||||
|
Unsafe.Add(ref bufferRef, destinationOffset) = |
||||
|
(byte)Av1Math.Clamp(Av1Math.RoundPowerOf2(prediction, 4), 0, 255); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
ref byte destinationRef = ref destination[0]; |
||||
|
for (int row = 0; row < height; row++) |
||||
|
{ |
||||
|
buffer.Slice(((row + 1) * BufferStride) + 1, width).CopyTo( |
||||
|
MemoryMarshal.CreateSpan(ref destinationRef, width)); |
||||
|
|
||||
|
destinationRef = ref Unsafe.Add(ref destinationRef, destinationStride); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue