mirror of https://github.com/SixLabors/ImageSharp
29 changed files with 1804 additions and 2 deletions
@ -0,0 +1,12 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal enum Av1CoefficientShape |
||||
|
{ |
||||
|
Default, |
||||
|
N2, |
||||
|
N4, |
||||
|
OnlyDc |
||||
|
} |
||||
@ -0,0 +1,157 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal class Av1DctDctInverseTransformer |
||||
|
{ |
||||
|
private const int UnitQuantizationShift = 2; |
||||
|
|
||||
|
internal static void InverseTransformAdd(ref int coefficients, Span<byte> readBuffer, int readStride, Span<byte> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters) |
||||
|
{ |
||||
|
Guard.IsTrue(transformFunctionParameters.TransformType == Av1TransformType.DctDct, nameof(transformFunctionParameters.TransformType), "This class implements DCT-DCT transformations only."); |
||||
|
|
||||
|
switch (transformFunctionParameters.TransformSize) |
||||
|
{ |
||||
|
case Av1TransformSize.Size4x4: |
||||
|
InverseWhalshHadamard4x4(ref coefficients, ref readBuffer[0], readStride, ref writeBuffer[0], writeStride, transformFunctionParameters.EndOfBuffer, transformFunctionParameters.BitDepth); |
||||
|
break; |
||||
|
default: |
||||
|
throw new NotImplementedException("Only 4x4 transformation size supported for now"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: highbd_iwht4x4_add
|
||||
|
/// </summary>
|
||||
|
private static void InverseWhalshHadamard4x4(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int endOfBuffer, int bitDepth) |
||||
|
{ |
||||
|
if (endOfBuffer > 1) |
||||
|
{ |
||||
|
InverseWhalshHadamard4x4Add16(ref input, ref destinationForRead, strideForRead, ref destinationForWrite, strideForWrite, bitDepth); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
InverseWhalshHadamard4x4Add1(ref input, ref destinationForRead, strideForRead, ref destinationForWrite, strideForWrite, bitDepth); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: svt_av1_highbd_iwht4x4_16_add_c
|
||||
|
/// </summary>
|
||||
|
private static void InverseWhalshHadamard4x4Add16(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int bitDepth) |
||||
|
{ |
||||
|
/* 4-point reversible, orthonormal inverse Walsh-Hadamard in 3.5 adds, |
||||
|
0.5 shifts per pixel. */ |
||||
|
int i; |
||||
|
Span<ushort> output = stackalloc ushort[16]; |
||||
|
ushort a1, b1, c1, d1, e1; |
||||
|
ref int ip = ref input; |
||||
|
ref ushort op = ref output[0]; |
||||
|
ref ushort opTmp = ref output[0]; |
||||
|
ref ushort destForRead = ref Unsafe.As<byte, ushort>(ref destinationForRead); |
||||
|
ref ushort destForWrite = ref Unsafe.As<byte, ushort>(ref destinationForWrite); |
||||
|
|
||||
|
for (i = 0; i < 4; i++) |
||||
|
{ |
||||
|
a1 = (ushort)(ip >> UnitQuantizationShift); |
||||
|
c1 = (ushort)(Unsafe.Add(ref ip, 1) >> UnitQuantizationShift); |
||||
|
d1 = (ushort)(Unsafe.Add(ref ip, 2) >> UnitQuantizationShift); |
||||
|
b1 = (ushort)(Unsafe.Add(ref ip, 3) >> UnitQuantizationShift); |
||||
|
a1 += c1; |
||||
|
d1 -= b1; |
||||
|
e1 = (ushort)((a1 - d1) >> 1); |
||||
|
b1 = (ushort)(e1 - b1); |
||||
|
c1 = (ushort)(e1 - c1); |
||||
|
a1 -= b1; |
||||
|
d1 += c1; |
||||
|
op = a1; |
||||
|
Unsafe.Add(ref op, 1) = b1; |
||||
|
Unsafe.Add(ref op, 2) = c1; |
||||
|
Unsafe.Add(ref op, 3) = d1; |
||||
|
ip = ref Unsafe.Add(ref ip, 4); |
||||
|
op = ref Unsafe.Add(ref op, 4); |
||||
|
} |
||||
|
|
||||
|
ip = opTmp; |
||||
|
for (i = 0; i < 4; i++) |
||||
|
{ |
||||
|
a1 = (ushort)ip; |
||||
|
c1 = (ushort)Unsafe.Add(ref ip, 4); |
||||
|
d1 = (ushort)Unsafe.Add(ref ip, 8); |
||||
|
b1 = (ushort)Unsafe.Add(ref ip, 12); |
||||
|
a1 += c1; |
||||
|
d1 -= b1; |
||||
|
e1 = (ushort)((a1 - d1) >> 1); |
||||
|
b1 = (ushort)(e1 - b1); |
||||
|
c1 = (ushort)(e1 - c1); |
||||
|
a1 -= b1; |
||||
|
d1 += c1; |
||||
|
/* Disabled in normal build |
||||
|
range_check_value(a1, (int8_t)(bd + 1)); |
||||
|
range_check_value(b1, (int8_t)(bd + 1)); |
||||
|
range_check_value(c1, (int8_t)(bd + 1)); |
||||
|
range_check_value(d1, (int8_t)(bd + 1)); |
||||
|
*/ |
||||
|
|
||||
|
destForWrite = ClipPixelAdd(destForRead, a1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead), b1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite * 2) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 2), c1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite * 3) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 3), d1, bitDepth); |
||||
|
|
||||
|
ip = ref Unsafe.Add(ref ip, 1); |
||||
|
destForRead = ref Unsafe.Add(ref destForRead, 1); |
||||
|
destForWrite = ref Unsafe.Add(ref destForWrite, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: svt_av1_highbd_iwht4x4_1_add_c
|
||||
|
/// </summary>
|
||||
|
private static void InverseWhalshHadamard4x4Add1(ref int input, ref byte destinationForRead, int strideForRead, ref byte destinationForWrite, int strideForWrite, int bitDepth) |
||||
|
{ |
||||
|
int i; |
||||
|
ushort a1, e1; |
||||
|
Span<int> tmp = stackalloc int[4]; |
||||
|
ref int ip = ref input; |
||||
|
ref int ipTmp = ref tmp[0]; |
||||
|
ref int op = ref tmp[0]; |
||||
|
ref ushort destForRead = ref Unsafe.As<byte, ushort>(ref destinationForRead); |
||||
|
ref ushort destForWrite = ref Unsafe.As<byte, ushort>(ref destinationForWrite); |
||||
|
|
||||
|
a1 = (ushort)(ip >> UnitQuantizationShift); |
||||
|
e1 = (ushort)(a1 >> 1); |
||||
|
a1 -= e1; |
||||
|
op = a1; |
||||
|
Unsafe.Add(ref op, 1) = e1; |
||||
|
Unsafe.Add(ref op, 2) = e1; |
||||
|
Unsafe.Add(ref op, 3) = e1; |
||||
|
|
||||
|
ip = ipTmp; |
||||
|
for (i = 0; i < 4; i++) |
||||
|
{ |
||||
|
e1 = (ushort)(ip >> 1); |
||||
|
a1 = (ushort)(ip - e1); |
||||
|
destForWrite = ClipPixelAdd(destForRead, a1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead), a1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite * 2) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 2), a1, bitDepth); |
||||
|
Unsafe.Add(ref destForWrite, strideForWrite * 3) = ClipPixelAdd(Unsafe.Add(ref destForRead, strideForRead * 3), a1, bitDepth); |
||||
|
ip = ref Unsafe.Add(ref ip, 1); |
||||
|
destForRead = ref Unsafe.Add(ref destForRead, 1); |
||||
|
destForWrite = ref Unsafe.Add(ref destForWrite, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static ushort ClipPixelAdd(ushort value, int trans, int bitDepth) |
||||
|
=> ClipPixel(value + trans, bitDepth); |
||||
|
|
||||
|
private static ushort ClipPixel(int value, int bitDepth) |
||||
|
=> bitDepth switch |
||||
|
{ |
||||
|
10 => (ushort)Av1Math.Clamp(value, 0, 1023), |
||||
|
12 => (ushort)Av1Math.Clamp(value, 0, 4095), |
||||
|
_ => (ushort)Av1Math.Clamp(value, 0, 255), |
||||
|
}; |
||||
|
} |
||||
@ -0,0 +1,276 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal class Av1ForwardTransformer |
||||
|
{ |
||||
|
private const int NewSqrt = 5793; |
||||
|
private const int NewSqrtBitCount = 12; |
||||
|
|
||||
|
private static readonly IAv1ForwardTransformer?[] Transformers = |
||||
|
[ |
||||
|
new Av1Dct4ForwardTransformer(), |
||||
|
new Av1Dct8ForwardTransformer(), |
||||
|
new Av1Dct16ForwardTransformer(), |
||||
|
new Av1Dct32ForwardTransformer(), |
||||
|
new Av1Dct64ForwardTransformer(), |
||||
|
new Av1Adst4ForwardTransformer(), |
||||
|
new Av1Adst8ForwardTransformer(), |
||||
|
new Av1Adst16ForwardTransformer(), |
||||
|
new Av1Adst32ForwardTransformer(), |
||||
|
new Av1Identity4ForwardTransformer(), |
||||
|
new Av1Identity8ForwardTransformer(), |
||||
|
new Av1Identity16ForwardTransformer(), |
||||
|
new Av1Identity32ForwardTransformer(), |
||||
|
new Av1Identity64ForwardTransformer(), |
||||
|
null |
||||
|
]; |
||||
|
|
||||
|
private static readonly int[] TemporaryCoefficientsBuffer = new int[64 * 64]; |
||||
|
|
||||
|
internal static void Transform2d(Span<short> input, Span<int> coefficients, uint stride, Av1TransformType transformType, Av1TransformSize transformSize, int bitDepth) |
||||
|
{ |
||||
|
Av1Transform2dFlipConfiguration config = new(transformType, transformSize); |
||||
|
ref int buffer = ref TemporaryCoefficientsBuffer[0]; |
||||
|
IAv1ForwardTransformer? columnTransformer = GetTransformer(config.TransformFunctionTypeColumn); |
||||
|
IAv1ForwardTransformer? rowTransformer = GetTransformer(config.TransformFunctionTypeRow); |
||||
|
if (columnTransformer != null && rowTransformer != null) |
||||
|
{ |
||||
|
Transform2dCore(columnTransformer, rowTransformer, ref input[0], stride, ref coefficients[0], config, ref buffer, bitDepth); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
throw new InvalidImageContentException($"Cannot find 1d transformer implementation for {config.TransformFunctionTypeColumn} or {config.TransformFunctionTypeRow}."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
internal static void Transform2dAvx2(Span<short> input, Span<int> coefficients, uint stride, Av1TransformType transformType, Av1TransformSize transformSize, int bitDepth) |
||||
|
{ |
||||
|
switch (transformSize) |
||||
|
{ |
||||
|
case Av1TransformSize.Size4x4: |
||||
|
// Too small for intrinsics, use the scalar codepath instead.
|
||||
|
Transform2d(input, coefficients, stride, transformType, transformSize, bitDepth); |
||||
|
break; |
||||
|
case Av1TransformSize.Size8x8: |
||||
|
Transform8x8Avx2(input, coefficients, stride, transformType, bitDepth); |
||||
|
break; |
||||
|
default: |
||||
|
Transform2d(input, coefficients, stride, transformType, transformSize, bitDepth); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: svt_av1_fwd_txfm2d_8x8_avx2
|
||||
|
/// </summary>
|
||||
|
private static void Transform8x8Avx2(Span<short> input, Span<int> coefficients, uint stride, Av1TransformType transformType, int bitDepth) |
||||
|
{ |
||||
|
Av1Transform2dFlipConfiguration config = new(transformType, Av1TransformSize.Size8x8); |
||||
|
Span<int> shift = config.Shift; |
||||
|
Span<Vector256<int>> inVector = stackalloc Vector256<int>[8]; |
||||
|
Span<Vector256<int>> outVector = stackalloc Vector256<int>[8]; |
||||
|
ref Vector256<int> inRef = ref inVector[0]; |
||||
|
ref Vector256<int> outRef = ref outVector[0]; |
||||
|
switch (transformType) |
||||
|
{ |
||||
|
case Av1TransformType.DctDct: |
||||
|
/* Pseudo code |
||||
|
Av1Dct8ForwardTransformer dct8 = new(); |
||||
|
LoadBuffer8x8(ref input[0], ref inRef, stride, 0, 0, shift[0]); |
||||
|
dct8.TransformAvx2(ref inRef, ref outRef, config.CosBitColumn, 1); |
||||
|
Column8x8Rounding(ref outRef, -shift[1]); |
||||
|
Transpose8x8Avx2(ref outRef, ref inRef); |
||||
|
dct8.TransformAvx2(ref inRef, ref outRef, config.CosBitRow, 1); |
||||
|
Transpose8x8Avx2(ref outRef, ref inRef); |
||||
|
WriteBuffer8x8(ref inRef, ref coefficients[0]); |
||||
|
break; |
||||
|
*/ |
||||
|
throw new NotImplementedException(); |
||||
|
default: |
||||
|
throw new NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static IAv1ForwardTransformer? GetTransformer(Av1TransformFunctionType transformerType) |
||||
|
=> Transformers[(int)transformerType]; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: av1_tranform_two_d_core_c
|
||||
|
/// </summary>
|
||||
|
private static void Transform2dCore<TColumn, TRow>(TColumn transformFunctionColumn, TRow transformFunctionRow, ref short input, uint inputStride, ref int output, Av1Transform2dFlipConfiguration config, ref int buf, int bitDepth) |
||||
|
where TColumn : IAv1ForwardTransformer |
||||
|
where TRow : IAv1ForwardTransformer |
||||
|
{ |
||||
|
int c, r; |
||||
|
|
||||
|
// Note when assigning txfm_size_col, we use the txfm_size from the
|
||||
|
// row configuration and vice versa. This is intentionally done to
|
||||
|
// accurately perform rectangular transforms. When the transform is
|
||||
|
// rectangular, the number of columns will be the same as the
|
||||
|
// txfm_size stored in the row cfg struct. It will make no difference
|
||||
|
// for square transforms.
|
||||
|
int transformColumnCount = config.TransformSize.GetWidth(); |
||||
|
int transformRowCount = config.TransformSize.GetHeight(); |
||||
|
int transformCount = transformColumnCount * transformRowCount; |
||||
|
|
||||
|
// Take the shift from the larger dimension in the rectangular case.
|
||||
|
Span<int> shift = config.Shift; |
||||
|
int rectangleType = GetRectangularRatio(transformColumnCount, transformRowCount); |
||||
|
Span<byte> stageRangeColumn = stackalloc byte[Av1Transform2dFlipConfiguration.MaxStageNumber]; |
||||
|
Span<byte> stageRangeRow = stackalloc byte[Av1Transform2dFlipConfiguration.MaxStageNumber]; |
||||
|
|
||||
|
// assert(cfg->stage_num_col <= MAX_TXFM_STAGE_NUM);
|
||||
|
// assert(cfg->stage_num_row <= MAX_TXFM_STAGE_NUM);
|
||||
|
config.GenerateStageRange(bitDepth); |
||||
|
|
||||
|
int cosBitColumn = config.CosBitColumn; |
||||
|
int cosBitRow = config.CosBitRow; |
||||
|
|
||||
|
// ASSERT(txfm_func_col != NULL);
|
||||
|
// ASSERT(txfm_func_row != NULL);
|
||||
|
// use output buffer as temp buffer
|
||||
|
ref int tempIn = ref output; |
||||
|
ref int tempOut = ref Unsafe.Add(ref output, transformRowCount); |
||||
|
|
||||
|
// Columns
|
||||
|
for (c = 0; c < transformColumnCount; ++c) |
||||
|
{ |
||||
|
if (!config.FlipUpsideDown) |
||||
|
{ |
||||
|
uint t = (uint)c; |
||||
|
for (r = 0; r < transformRowCount; ++r) |
||||
|
{ |
||||
|
Unsafe.Add(ref tempIn, r) = Unsafe.Add(ref input, t); |
||||
|
t += inputStride; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
uint t = (uint)(c + ((transformRowCount - 1) * (int)inputStride)); |
||||
|
for (r = 0; r < transformRowCount; ++r) |
||||
|
{ |
||||
|
// flip upside down
|
||||
|
Unsafe.Add(ref tempIn, r) = Unsafe.Add(ref input, t); |
||||
|
t -= inputStride; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
RoundShiftArray(ref tempIn, transformRowCount, -shift[0]); // NM svt_av1_round_shift_array_c
|
||||
|
transformFunctionColumn.Transform(ref tempIn, ref tempOut, cosBitColumn, stageRangeColumn); |
||||
|
RoundShiftArray(ref tempOut, transformRowCount, -shift[1]); // NM svt_av1_round_shift_array_c
|
||||
|
if (!config.FlipLeftToRight) |
||||
|
{ |
||||
|
int t = c; |
||||
|
for (r = 0; r < transformRowCount; ++r) |
||||
|
{ |
||||
|
Unsafe.Add(ref buf, t) = Unsafe.Add(ref tempOut, r); |
||||
|
t += transformColumnCount; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
int t = transformColumnCount - c - 1; |
||||
|
for (r = 0; r < transformRowCount; ++r) |
||||
|
{ |
||||
|
// flip from left to right
|
||||
|
Unsafe.Add(ref buf, t) = Unsafe.Add(ref tempOut, r); |
||||
|
t += transformColumnCount; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Rows
|
||||
|
for (r = 0; r < transformRowCount; ++r) |
||||
|
{ |
||||
|
transformFunctionRow.Transform(ref Unsafe.Add(ref buf, r * transformColumnCount), ref Unsafe.Add(ref output, r * transformColumnCount), cosBitRow, stageRangeRow); |
||||
|
RoundShiftArray(ref Unsafe.Add(ref output, r * transformColumnCount), transformColumnCount, -shift[2]); |
||||
|
|
||||
|
if (Math.Abs(rectangleType) == 1) |
||||
|
{ |
||||
|
// Multiply everything by Sqrt2 if the transform is rectangular and the
|
||||
|
// size difference is a factor of 2.
|
||||
|
for (c = 0; c < transformColumnCount; ++c) |
||||
|
{ |
||||
|
ref int current = ref Unsafe.Add(ref output, (r * transformColumnCount) + c); |
||||
|
current = Av1Math.RoundShift((long)current * NewSqrt, NewSqrtBitCount); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void RoundShiftArray(ref int arr, int size, int bit) |
||||
|
{ |
||||
|
if (bit == 0) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
nuint sz = (nuint)size; |
||||
|
if (bit > 0) |
||||
|
{ |
||||
|
for (nuint i = 0; i < sz; i++) |
||||
|
{ |
||||
|
ref int a = ref Unsafe.Add(ref arr, i); |
||||
|
a = Av1Math.RoundShift(a, bit); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
for (nuint i = 0; i < sz; i++) |
||||
|
{ |
||||
|
ref int a = ref Unsafe.Add(ref arr, i); |
||||
|
a *= 1 << (-bit); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: get_rect_tx_log_ratio
|
||||
|
/// </summary>
|
||||
|
public static int GetRectangularRatio(int col, int row) |
||||
|
{ |
||||
|
if (col == row) |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
if (col > row) |
||||
|
{ |
||||
|
if (col == row * 2) |
||||
|
{ |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
if (col == row * 4) |
||||
|
{ |
||||
|
return 2; |
||||
|
} |
||||
|
|
||||
|
Guard.IsTrue(false, nameof(row), "Unsupported transform size"); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (row == col * 2) |
||||
|
{ |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (row == col * 4) |
||||
|
{ |
||||
|
return -2; |
||||
|
} |
||||
|
|
||||
|
Guard.IsTrue(false, nameof(row), "Unsupported transform size"); |
||||
|
} |
||||
|
|
||||
|
return 0; // Invalid
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal static class Av1SinusConstants |
||||
|
{ |
||||
|
public const int MinimumCosinusBit = 10; |
||||
|
|
||||
|
// av1_cospi_arr[i][j] = (int32_t)round(cos(M_PI*j/128) * (1<<(cos_bit_min+i)));
|
||||
|
private static readonly int[][] CosinusPiArray = |
||||
|
[ |
||||
|
[ |
||||
|
1024, 1024, 1023, 1021, 1019, 1016, 1013, 1009, 1004, 999, 993, 987, 980, 972, 964, 955, |
||||
|
946, 936, 926, 915, 903, 891, 878, 865, 851, 837, 822, 807, 792, 775, 759, 742, |
||||
|
724, 706, 688, 669, 650, 630, 610, 590, 569, 548, 526, 505, 483, 460, 438, 415, |
||||
|
392, 369, 345, 321, 297, 273, 249, 224, 200, 175, 150, 125, 100, 75, 50, 25 |
||||
|
], |
||||
|
[ |
||||
|
2048, 2047, 2046, 2042, 2038, 2033, 2026, 2018, 2009, 1998, 1987, 1974, 1960, 1945, 1928, 1911, |
||||
|
1892, 1872, 1851, 1829, 1806, 1782, 1757, 1730, 1703, 1674, 1645, 1615, 1583, 1551, 1517, 1483, |
||||
|
1448, 1412, 1375, 1338, 1299, 1260, 1220, 1179, 1138, 1096, 1053, 1009, 965, 921, 876, 830, |
||||
|
784, 737, 690, 642, 595, 546, 498, 449, 400, 350, 301, 251, 201, 151, 100, 50 |
||||
|
], |
||||
|
[ |
||||
|
4096, 4095, 4091, 4085, 4076, 4065, 4052, 4036, 4017, 3996, 3973, 3948, 3920, 3889, 3857, 3822, |
||||
|
3784, 3745, 3703, 3659, 3612, 3564, 3513, 3461, 3406, 3349, 3290, 3229, 3166, 3102, 3035, 2967, |
||||
|
2896, 2824, 2751, 2675, 2598, 2520, 2440, 2359, 2276, 2191, 2106, 2019, 1931, 1842, 1751, 1660, |
||||
|
1567, 1474, 1380, 1285, 1189, 1092, 995, 897, 799, 700, 601, 501, 401, 301, 201, 101 |
||||
|
], |
||||
|
[ |
||||
|
8192, 8190, 8182, 8170, 8153, 8130, 8103, 8071, 8035, 7993, 7946, 7895, 7839, 7779, 7713, 7643, |
||||
|
7568, 7489, 7405, 7317, 7225, 7128, 7027, 6921, 6811, 6698, 6580, 6458, 6333, 6203, 6070, 5933, |
||||
|
5793, 5649, 5501, 5351, 5197, 5040, 4880, 4717, 4551, 4383, 4212, 4038, 3862, 3683, 3503, 3320, |
||||
|
3135, 2948, 2760, 2570, 2378, 2185, 1990, 1795, 1598, 1401, 1202, 1003, 803, 603, 402, 201 |
||||
|
], |
||||
|
[ |
||||
|
16384, 16379, 16364, 16340, 16305, 16261, 16207, 16143, 16069, 15986, 15893, 15791, 15679, 15557, 15426, 15286, |
||||
|
15137, 14978, 14811, 14635, 14449, 14256, 14053, 13842, 13623, 13395, 13160, 12916, 12665, 12406, 12140, 11866, |
||||
|
11585, 11297, 11003, 10702, 10394, 10080, 9760, 9434, 9102, 8765, 8423, 8076, 7723, 7366, 7005, 6639, |
||||
|
6270, 5897, 5520, 5139, 4756, 4370, 3981, 3590, 3196, 2801, 2404, 2006, 1606, 1205, 804, 402 |
||||
|
], |
||||
|
[ |
||||
|
32768, 32758, 32729, 32679, 32610, 32522, 32413, 32286, 32138, 31972, 31786, 31581, 31357, 31114, 30853, 30572, |
||||
|
30274, 29957, 29622, 29269, 28899, 28511, 28106, 27684, 27246, 26791, 26320, 25833, 25330, 24812, 24279, 23732, |
||||
|
23170, 22595, 22006, 21403, 20788, 20160, 19520, 18868, 18205, 17531, 16846, 16151, 15447, 14733, 14010, 13279, |
||||
|
12540, 11793, 11039, 10279, 9512, 8740, 7962, 7180, 6393, 5602, 4808, 4011, 3212, 2411, 1608, 804 |
||||
|
], |
||||
|
[ |
||||
|
65536, 65516, 65457, 65358, 65220, 65043, 64827, 64571, 64277, 63944, 63572, 63162, 62714, 62228, 61705, 61145, |
||||
|
60547, 59914, 59244, 58538, 57798, 57022, 56212, 55368, 54491, 53581, 52639, 51665, 50660, 49624, 48559, 47464, |
||||
|
46341, 45190, 44011, 42806, 41576, 40320, 39040, 37736, 36410, 35062, 33692, 32303, 30893, 29466, 28020, 26558, |
||||
|
25080, 23586, 22078, 20557, 19024, 17479, 15924, 14359, 12785, 11204, 9616, 8022, 6424, 4821, 3216, 1608 |
||||
|
] |
||||
|
]; |
||||
|
|
||||
|
// svt_aom_eb_av1_sinpi_arr_data[i][j] = (int32_t)round((sqrt(2) * sin(j*Pi/9) * 2 / 3) * (1
|
||||
|
// << (cos_bit_min + i))) modified so that elements j=1,2 sum to element j=4.
|
||||
|
private static readonly int[][] SinusPiArray = |
||||
|
[ |
||||
|
[0, 330, 621, 836, 951], |
||||
|
[0, 660, 1241, 1672, 1901], |
||||
|
[0, 1321, 2482, 3344, 3803], |
||||
|
[0, 2642, 4964, 6689, 7606], |
||||
|
[0, 5283, 9929, 13377, 15212], |
||||
|
[0, 10566, 19858, 26755, 30424], |
||||
|
[0, 21133, 39716, 53510, 60849] |
||||
|
]; |
||||
|
|
||||
|
public static Span<int> CosinusPi(int n) => CosinusPiArray[n - MinimumCosinusBit]; |
||||
|
|
||||
|
public static Span<int> SinusPi(int n) => SinusPiArray[n - MinimumCosinusBit]; |
||||
|
} |
||||
@ -0,0 +1,313 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Drawing; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal class Av1Transform2dFlipConfiguration |
||||
|
{ |
||||
|
public const int MaxStageNumber = 12; |
||||
|
private const int SmallestTransformSizeLog2 = 2; |
||||
|
|
||||
|
private static readonly Av1TransformType1d[] VerticalType = |
||||
|
[ |
||||
|
Av1TransformType1d.Dct, |
||||
|
Av1TransformType1d.Adst, |
||||
|
Av1TransformType1d.Dct, |
||||
|
Av1TransformType1d.Adst, |
||||
|
Av1TransformType1d.FlipAdst, |
||||
|
Av1TransformType1d.Dct, |
||||
|
Av1TransformType1d.FlipAdst, |
||||
|
Av1TransformType1d.Adst, |
||||
|
Av1TransformType1d.FlipAdst, |
||||
|
Av1TransformType1d.Identity, |
||||
|
Av1TransformType1d.Dct, |
||||
|
Av1TransformType1d.Identity, |
||||
|
Av1TransformType1d.Adst, |
||||
|
Av1TransformType1d.Identity, |
||||
|
Av1TransformType1d.FlipAdst, |
||||
|
Av1TransformType1d.Identity, |
||||
|
]; |
||||
|
|
||||
|
private static readonly Av1TransformType1d[] HorizontalType = |
||||
|
[ |
||||
|
Av1TransformType1d.Dct, |
||||
|
Av1TransformType1d.Dct, |
||||
|
Av1TransformType1d.Adst, |
||||
|
Av1TransformType1d.Adst, |
||||
|
Av1TransformType1d.Dct, |
||||
|
Av1TransformType1d.FlipAdst, |
||||
|
Av1TransformType1d.FlipAdst, |
||||
|
Av1TransformType1d.FlipAdst, |
||||
|
Av1TransformType1d.Adst, |
||||
|
Av1TransformType1d.Identity, |
||||
|
Av1TransformType1d.Identity, |
||||
|
Av1TransformType1d.Dct, |
||||
|
Av1TransformType1d.Identity, |
||||
|
Av1TransformType1d.Adst, |
||||
|
Av1TransformType1d.Identity, |
||||
|
Av1TransformType1d.FlipAdst, |
||||
|
]; |
||||
|
|
||||
|
private static readonly int[][] ShiftMap = |
||||
|
[ |
||||
|
[2, 0, 0], // 4x4
|
||||
|
[2, -1, 0], // 8x8
|
||||
|
[2, -2, 0], // 16x16
|
||||
|
[2, -4, 0], // 32x32
|
||||
|
[0, -2, -2], // 64x64
|
||||
|
[2, -1, 0], // 4x8
|
||||
|
[2, -1, 0], // 8x4
|
||||
|
[2, -2, 0], // 8x16
|
||||
|
[2, -2, 0], // 16x8
|
||||
|
[2, -4, 0], // 16x32
|
||||
|
[2, -4, 0], // 32x16
|
||||
|
[0, -2, -2], // 32x64
|
||||
|
[2, -4, -2], // 64x32
|
||||
|
[2, -1, 0], // 4x16
|
||||
|
[2, -1, 0], // 16x4
|
||||
|
[2, -2, 0], // 8x32
|
||||
|
[2, -2, 0], // 32x8
|
||||
|
[0, -2, 0], // 16x64
|
||||
|
[2, -4, 0], // 64x16
|
||||
|
]; |
||||
|
|
||||
|
private static readonly int[][] CosBitColumnMap = |
||||
|
[[13, 13, 13, 0, 0], [13, 13, 13, 12, 0], [13, 13, 13, 12, 13], [0, 13, 13, 12, 13], [0, 0, 13, 12, 13]]; |
||||
|
|
||||
|
private static readonly int[][] CosBitRowMap = |
||||
|
[[13, 13, 12, 0, 0], [13, 13, 13, 12, 0], [13, 13, 12, 13, 12], [0, 12, 13, 12, 11], [0, 0, 12, 11, 10]]; |
||||
|
|
||||
|
private static readonly Av1TransformFunctionType[][] TransformFunctionTypeMap = |
||||
|
[ |
||||
|
[Av1TransformFunctionType.Dct4, Av1TransformFunctionType.Adst4, Av1TransformFunctionType.Adst4, Av1TransformFunctionType.Identity4], |
||||
|
[Av1TransformFunctionType.Dct8, Av1TransformFunctionType.Adst8, Av1TransformFunctionType.Adst8, Av1TransformFunctionType.Identity8], |
||||
|
[Av1TransformFunctionType.Dct16, Av1TransformFunctionType.Adst16, Av1TransformFunctionType.Adst16, Av1TransformFunctionType.Identity16], |
||||
|
[Av1TransformFunctionType.Dct32, Av1TransformFunctionType.Adst32, Av1TransformFunctionType.Adst32, Av1TransformFunctionType.Identity32], |
||||
|
[Av1TransformFunctionType.Dct64, Av1TransformFunctionType.Invalid, Av1TransformFunctionType.Invalid, Av1TransformFunctionType.Identity64] |
||||
|
]; |
||||
|
|
||||
|
private static readonly int[] StageNumberList = |
||||
|
[ |
||||
|
4, // TXFM_TYPE_DCT4
|
||||
|
6, // TXFM_TYPE_DCT8
|
||||
|
8, // TXFM_TYPE_DCT16
|
||||
|
10, // TXFM_TYPE_DCT32
|
||||
|
12, // TXFM_TYPE_DCT64
|
||||
|
7, // TXFM_TYPE_ADST4
|
||||
|
8, // TXFM_TYPE_ADST8
|
||||
|
10, // TXFM_TYPE_ADST16
|
||||
|
12, // TXFM_TYPE_ADST32
|
||||
|
1, // TXFM_TYPE_IDENTITY4
|
||||
|
1, // TXFM_TYPE_IDENTITY8
|
||||
|
1, // TXFM_TYPE_IDENTITY16
|
||||
|
1, // TXFM_TYPE_IDENTITY32
|
||||
|
1, // TXFM_TYPE_IDENTITY64
|
||||
|
]; |
||||
|
|
||||
|
private static readonly int[][] RangeMulti2List = |
||||
|
[ |
||||
|
[0, 2, 3, 3], // fdct4_range_mult2
|
||||
|
[0, 2, 4, 5, 5, 5], // fdct8_range_mult2
|
||||
|
[0, 2, 4, 6, 7, 7, 7, 7], // fdct16_range_mult2
|
||||
|
[0, 2, 4, 6, 8, 9, 9, 9, 9, 9], // fdct32_range_mult2
|
||||
|
[0, 2, 4, 6, 8, 10, 11, 11, 11, 11, 11, 11], // fdct64_range_mult2
|
||||
|
[0, 2, 4, 3, 3, 3, 3], // fadst4_range_mult2
|
||||
|
[0, 0, 1, 3, 3, 5, 5, 5], // fadst8_range_mult2
|
||||
|
[0, 0, 1, 3, 3, 5, 5, 7, 7, 7], // fadst16_range_mult2
|
||||
|
[0, 0, 1, 3, 3, 5, 5, 7, 7, 9, 9, 9], // fadst32_range_mult2
|
||||
|
[1], // fidtx4_range_mult2
|
||||
|
[2], // fidtx8_range_mult2
|
||||
|
[3], // fidtx16_range_mult2
|
||||
|
[4], // fidtx32_range_mult2
|
||||
|
[5], // fidtx64_range_mult2
|
||||
|
]; |
||||
|
|
||||
|
private readonly int[] shift; |
||||
|
|
||||
|
public Av1Transform2dFlipConfiguration(Av1TransformType transformType, Av1TransformSize transformSize) |
||||
|
{ |
||||
|
this.TransformSize = transformSize; |
||||
|
this.TransformType = transformType; |
||||
|
this.SetFlip(transformType); |
||||
|
Av1TransformType1d tx_type_1d_col = VerticalType[(int)transformType]; |
||||
|
Av1TransformType1d tx_type_1d_row = HorizontalType[(int)transformType]; |
||||
|
int txw_idx = transformSize.GetBlockWidthLog2() - SmallestTransformSizeLog2; |
||||
|
int txh_idx = transformSize.GetBlockHeightLog2() - SmallestTransformSizeLog2; |
||||
|
this.shift = ShiftMap[(int)transformSize]; |
||||
|
this.CosBitColumn = CosBitColumnMap[txw_idx][txh_idx]; |
||||
|
this.CosBitRow = CosBitRowMap[txw_idx][txh_idx]; |
||||
|
this.TransformFunctionTypeColumn = TransformFunctionTypeMap[txh_idx][(int)tx_type_1d_col]; |
||||
|
this.TransformFunctionTypeRow = TransformFunctionTypeMap[txw_idx][(int)tx_type_1d_row]; |
||||
|
this.StageNumberColumn = StageNumberList[(int)this.TransformFunctionTypeColumn]; |
||||
|
this.StageNumberRow = StageNumberList[(int)this.TransformFunctionTypeRow]; |
||||
|
this.StageRangeColumn = new int[12]; |
||||
|
this.StageRangeRow = new int[12]; |
||||
|
this.NonScaleRange(); |
||||
|
} |
||||
|
|
||||
|
public int CosBitColumn { get; } |
||||
|
|
||||
|
public int CosBitRow { get; } |
||||
|
|
||||
|
public Av1TransformFunctionType TransformFunctionTypeColumn { get; } |
||||
|
|
||||
|
public Av1TransformFunctionType TransformFunctionTypeRow { get; } |
||||
|
|
||||
|
public int StageNumberColumn { get; } |
||||
|
|
||||
|
public int StageNumberRow { get; } |
||||
|
|
||||
|
public Av1TransformSize TransformSize { get; } |
||||
|
|
||||
|
public Av1TransformType TransformType { get; } |
||||
|
|
||||
|
public bool FlipUpsideDown { get; private set; } |
||||
|
|
||||
|
public bool FlipLeftToRight { get; private set; } |
||||
|
|
||||
|
public Span<int> Shift => this.shift; |
||||
|
|
||||
|
public int[] StageRangeColumn { get; } |
||||
|
|
||||
|
public int[] StageRangeRow { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: svt_av1_gen_fwd_stage_range
|
||||
|
/// </summary>
|
||||
|
public void GenerateStageRange(int bitDepth) |
||||
|
{ |
||||
|
// Take the shift from the larger dimension in the rectangular case.
|
||||
|
Span<int> shift = this.Shift; |
||||
|
|
||||
|
// i < MAX_TXFM_STAGE_NUM will mute above array bounds warning
|
||||
|
for (int i = 0; i < this.StageNumberColumn && i < MaxStageNumber; ++i) |
||||
|
{ |
||||
|
this.StageRangeColumn[i] = this.StageRangeColumn[i] + shift[0] + bitDepth + 1; |
||||
|
} |
||||
|
|
||||
|
// i < MAX_TXFM_STAGE_NUM will mute above array bounds warning
|
||||
|
for (int i = 0; i < this.StageNumberRow && i < MaxStageNumber; ++i) |
||||
|
{ |
||||
|
this.StageRangeRow[i] = this.StageRangeRow[i] + shift[0] + shift[1] + bitDepth + 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: is_txfm_allowed
|
||||
|
/// </summary>
|
||||
|
public bool IsAllowed() |
||||
|
{ |
||||
|
Av1TransformType[] supportedTypes = |
||||
|
[ |
||||
|
Av1TransformType.DctDct, |
||||
|
Av1TransformType.AdstDct, |
||||
|
Av1TransformType.DctAdst, |
||||
|
Av1TransformType.AdstAdst, |
||||
|
Av1TransformType.FlipAdstDct, |
||||
|
Av1TransformType.DctFlipAdst, |
||||
|
Av1TransformType.FlipAdstFlipAdst, |
||||
|
Av1TransformType.AdstFlipAdst, |
||||
|
Av1TransformType.FlipAdstAdst, |
||||
|
Av1TransformType.Identity, |
||||
|
Av1TransformType.VerticalDct, |
||||
|
Av1TransformType.HorizontalDct, |
||||
|
Av1TransformType.VerticalAdst, |
||||
|
Av1TransformType.HorizontalAdst, |
||||
|
Av1TransformType.VerticalFlipAdst, |
||||
|
Av1TransformType.HorizontalFlipAdst, |
||||
|
]; |
||||
|
|
||||
|
switch (this.TransformSize) |
||||
|
{ |
||||
|
case Av1TransformSize.Size32x32: |
||||
|
supportedTypes = [Av1TransformType.DctDct, Av1TransformType.Identity, Av1TransformType.VerticalDct, Av1TransformType.HorizontalDct]; |
||||
|
break; |
||||
|
case Av1TransformSize.Size32x64: |
||||
|
case Av1TransformSize.Size64x32: |
||||
|
case Av1TransformSize.Size16x64: |
||||
|
case Av1TransformSize.Size64x16: |
||||
|
supportedTypes = [Av1TransformType.DctDct]; |
||||
|
break; |
||||
|
case Av1TransformSize.Size16x32: |
||||
|
case Av1TransformSize.Size32x16: |
||||
|
case Av1TransformSize.Size64x64: |
||||
|
case Av1TransformSize.Size8x32: |
||||
|
case Av1TransformSize.Size32x8: |
||||
|
supportedTypes = [Av1TransformType.DctDct, Av1TransformType.Identity]; |
||||
|
break; |
||||
|
default: |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return supportedTypes.Contains(this.TransformType); |
||||
|
} |
||||
|
|
||||
|
private void SetFlip(Av1TransformType transformType) |
||||
|
{ |
||||
|
switch (transformType) |
||||
|
{ |
||||
|
case Av1TransformType.DctDct: |
||||
|
case Av1TransformType.AdstDct: |
||||
|
case Av1TransformType.DctAdst: |
||||
|
case Av1TransformType.AdstAdst: |
||||
|
this.FlipUpsideDown = false; |
||||
|
this.FlipLeftToRight = false; |
||||
|
break; |
||||
|
case Av1TransformType.Identity: |
||||
|
case Av1TransformType.VerticalDct: |
||||
|
case Av1TransformType.HorizontalDct: |
||||
|
case Av1TransformType.VerticalAdst: |
||||
|
case Av1TransformType.HorizontalAdst: |
||||
|
this.FlipUpsideDown = false; |
||||
|
this.FlipLeftToRight = false; |
||||
|
break; |
||||
|
case Av1TransformType.FlipAdstDct: |
||||
|
case Av1TransformType.FlipAdstAdst: |
||||
|
case Av1TransformType.VerticalFlipAdst: |
||||
|
this.FlipUpsideDown = true; |
||||
|
this.FlipLeftToRight = false; |
||||
|
break; |
||||
|
case Av1TransformType.DctFlipAdst: |
||||
|
case Av1TransformType.AdstFlipAdst: |
||||
|
case Av1TransformType.HorizontalFlipAdst: |
||||
|
this.FlipUpsideDown = false; |
||||
|
this.FlipLeftToRight = true; |
||||
|
break; |
||||
|
case Av1TransformType.FlipAdstFlipAdst: |
||||
|
this.FlipUpsideDown = true; |
||||
|
this.FlipLeftToRight = true; |
||||
|
break; |
||||
|
default: |
||||
|
Guard.IsTrue(false, nameof(transformType), "Unknown transform type for determining flip."); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: set_fwd_txfm_non_scale_range
|
||||
|
/// </summary>
|
||||
|
private void NonScaleRange() |
||||
|
{ |
||||
|
Span<int> range_mult2_col = RangeMulti2List[(int)this.TransformFunctionTypeColumn]; |
||||
|
if (this.TransformFunctionTypeColumn != Av1TransformFunctionType.Invalid) |
||||
|
{ |
||||
|
int stage_num_col = this.StageNumberColumn; |
||||
|
for (int i = 0; i < stage_num_col; ++i) |
||||
|
{ |
||||
|
this.StageRangeColumn[i] = (range_mult2_col[i] + 1) >> 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (this.TransformFunctionTypeRow != Av1TransformFunctionType.Invalid) |
||||
|
{ |
||||
|
int stage_num_row = this.StageNumberRow; |
||||
|
Span<int> range_mult2_row = RangeMulti2List[(int)this.TransformFunctionTypeRow]; |
||||
|
for (int i = 0; i < stage_num_row; ++i) |
||||
|
{ |
||||
|
this.StageRangeRow[i] = (range_mult2_col[this.StageNumberColumn - 1] + range_mult2_row[i] + 1) >> 1; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal class Av1TransformFunctionParameters |
||||
|
{ |
||||
|
public Av1TransformType TransformType { get; internal set; } |
||||
|
|
||||
|
public Av1TransformSize TransformSize { get; internal set; } |
||||
|
|
||||
|
public int EndOfBuffer { get; internal set; } |
||||
|
|
||||
|
public bool IsLossless { get; internal set; } |
||||
|
|
||||
|
public int BitDepth { get; internal set; } |
||||
|
|
||||
|
public bool Is16BitPipeline { get; internal set; } |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal enum Av1TransformFunctionType |
||||
|
{ |
||||
|
Dct4, |
||||
|
Dct8, |
||||
|
Dct16, |
||||
|
Dct32, |
||||
|
Dct64, |
||||
|
Adst4, |
||||
|
Adst8, |
||||
|
Adst16, |
||||
|
Adst32, |
||||
|
Identity4, |
||||
|
Identity8, |
||||
|
Identity16, |
||||
|
Identity32, |
||||
|
Identity64, |
||||
|
Invalid, |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal enum Av1TransformType1d |
||||
|
{ |
||||
|
Dct, |
||||
|
Adst, |
||||
|
FlipAdst, |
||||
|
Identity |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Adst16ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Adst32ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Adst4ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Adst8ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Dct16ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Dct32ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.Intrinsics; |
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Dct4ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException("Too small block for Vector implementation, use TransformSse() method instead."); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVT: fdct4x4_sse4_1
|
||||
|
/// </summary>
|
||||
|
public static void TransformSse(ref Vector128<int> input, ref Vector128<int> output, byte cosBit, int columnNumber) |
||||
|
{ |
||||
|
/* |
||||
|
// We only use stage-2 bit;
|
||||
|
// shift[0] is used in load_buffer_4x4()
|
||||
|
// shift[1] is used in txfm_func_col()
|
||||
|
// shift[2] is used in txfm_func_row()
|
||||
|
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
||||
|
Vector128<int> cospi32 = Vector128.Create<int>(cospi[32]); |
||||
|
Vector128<int> cospi48 = Vector128.Create<int>(cospi[48]); |
||||
|
Vector128<int> cospi16 = Vector128.Create<int>(cospi[16]); |
||||
|
Vector128<int> rnding = Vector128.Create<int>(1 << (cosBit - 1)); |
||||
|
Vector128<int> s0, s1, s2, s3; |
||||
|
Vector128<int> u0, u1, u2, u3; |
||||
|
Vector128<int> v0, v1, v2, v3; |
||||
|
|
||||
|
int endidx = 3 * columnNumber; |
||||
|
s0 = Sse41.Add(input, Unsafe.Add(ref input, endidx)); |
||||
|
s3 = Sse41.Subtract(input, Unsafe.Add(ref input, endidx)); |
||||
|
endidx -= columnNumber; |
||||
|
s1 = Sse41.Add(Unsafe.Add(ref input, columnNumber), Unsafe.Add(ref input, endidx)); |
||||
|
s2 = Sse41.Subtract(Unsafe.Add(ref input, columnNumber), Unsafe.Add(ref input, endidx)); |
||||
|
|
||||
|
// btf_32_sse4_1_type0(cospi32, cospi32, s[01], u[02], bit);
|
||||
|
u0 = Sse41.MultiplyLow(s0, cospi32); |
||||
|
u1 = Sse41.MultiplyLow(s1, cospi32); |
||||
|
u2 = Sse41.Add(u0, u1); |
||||
|
v0 = Sse41.Subtract(u0, u1); |
||||
|
|
||||
|
u3 = Sse41.Add(u2, rnding); |
||||
|
v1 = Sse41.Add(v0, rnding); |
||||
|
|
||||
|
u0 = Sse41.ShiftRightArithmetic(u3, cosBit); |
||||
|
u2 = Sse41.ShiftRightArithmetic(v1, cosBit); |
||||
|
|
||||
|
// btf_32_sse4_1_type1(cospi48, cospi16, s[23], u[13], bit);
|
||||
|
v0 = Sse41.MultiplyLow(s2, cospi48); |
||||
|
v1 = Sse41.MultiplyLow(s3, cospi16); |
||||
|
v2 = Sse41.Add(v0, v1); |
||||
|
|
||||
|
v3 = Sse41.Add(v2, rnding); |
||||
|
u1 = Sse41.ShiftRightArithmetic(v3, cosBit); |
||||
|
|
||||
|
v0 = Sse41.MultiplyLow(s2, cospi16); |
||||
|
v1 = Sse41.MultiplyLow(s3, cospi48); |
||||
|
v2 = Sse41.Subtract(v1, v0); |
||||
|
|
||||
|
v3 = Sse41.Add(v2, rnding); |
||||
|
u3 = Sse41.ShiftRightArithmetic(v3, cosBit); |
||||
|
|
||||
|
// Note: shift[1] and shift[2] are zeros
|
||||
|
|
||||
|
// Transpose 4x4 32-bit
|
||||
|
v0 = Sse41.UnpackLow(u0, u1); |
||||
|
v1 = Sse41.UnpackHigh(u0, u1); |
||||
|
v2 = Sse41.UnpackLow(u2, u3); |
||||
|
v3 = Sse41.UnpackHigh(u2, u3); |
||||
|
|
||||
|
output = Sse41.UnpackLow(v0.AsInt64(), v2.AsInt64()).AsInt32(); |
||||
|
Unsafe.Add(ref output, 1) = Sse41.UnpackHigh(v0.AsInt64(), v2.AsInt64()).AsInt32(); |
||||
|
Unsafe.Add(ref output, 2) = Sse41.UnpackLow(v1.AsInt64(), v3.AsInt64()).AsInt32(); |
||||
|
Unsafe.Add(ref output, 3) = Sse41.UnpackHigh(v1.AsInt64(), v3.AsInt64()).AsInt32(); |
||||
|
*/ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Dct64ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Dct8ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Identity16ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Identity32ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Identity4ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Identity64ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
||||
|
|
||||
|
internal class Av1Identity8ForwardTransformer : IAv1ForwardTransformer |
||||
|
{ |
||||
|
public void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
||||
|
=> throw new NotImplementedException(); |
||||
|
|
||||
|
public void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber) |
||||
|
=> throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Tiling; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal static class ForwardTransformerFactory |
||||
|
{ |
||||
|
internal static void EstimateTransform( |
||||
|
Span<short> residualBuffer, |
||||
|
uint residualStride, |
||||
|
Span<int> coefficientBuffer, |
||||
|
uint coefficientStride, |
||||
|
Av1TransformSize transformSize, |
||||
|
ref ulong threeQuadEnergy, |
||||
|
int bitDepth, |
||||
|
Av1TransformType transformType, |
||||
|
Av1PlaneType componentType, |
||||
|
Av1CoefficientShape transformCoefficientShape) |
||||
|
{ |
||||
|
switch (transformCoefficientShape) |
||||
|
{ |
||||
|
case Av1CoefficientShape.Default: |
||||
|
EstimateTransformDefault(residualBuffer, residualStride, coefficientBuffer, coefficientStride, transformSize, ref threeQuadEnergy, bitDepth, transformType, componentType); |
||||
|
break; |
||||
|
case Av1CoefficientShape.N2: |
||||
|
EstimateTransformN2(residualBuffer, residualStride, coefficientBuffer, coefficientStride, transformSize, ref threeQuadEnergy, bitDepth, transformType, componentType); |
||||
|
break; |
||||
|
case Av1CoefficientShape.N4: |
||||
|
EstimateTransformN4(residualBuffer, residualStride, coefficientBuffer, coefficientStride, transformSize, ref threeQuadEnergy, bitDepth, transformType, componentType); |
||||
|
break; |
||||
|
case Av1CoefficientShape.OnlyDc: |
||||
|
EstimateTransformOnlyDc(residualBuffer, residualStride, coefficientBuffer, coefficientStride, transformSize, ref threeQuadEnergy, bitDepth, transformType, componentType); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void EstimateTransformDefault( |
||||
|
Span<short> residualBuffer, |
||||
|
uint residualStride, |
||||
|
Span<int> coefficientBuffer, |
||||
|
uint coefficientStride, |
||||
|
Av1TransformSize transformSize, |
||||
|
ref ulong threeQuadEnergy, |
||||
|
int bitDepth, |
||||
|
Av1TransformType transformType, |
||||
|
Av1PlaneType componentType) |
||||
|
=> Av1ForwardTransformer.Transform2d(residualBuffer, coefficientBuffer, residualStride, transformType, transformSize, bitDepth); |
||||
|
|
||||
|
private static void EstimateTransformN2(Span<short> residualBuffer, uint residualStride, Span<int> coefficientBuffer, uint coefficientStride, Av1TransformSize transformSize, ref ulong threeQuadEnergy, int bitDepth, Av1TransformType transformType, Av1PlaneType componentType) => throw new NotImplementedException(); |
||||
|
|
||||
|
private static void EstimateTransformN4(Span<short> residualBuffer, uint residualStride, Span<int> coefficientBuffer, uint coefficientStride, Av1TransformSize transformSize, ref ulong threeQuadEnergy, int bitDepth, Av1TransformType transformType, Av1PlaneType componentType) => throw new NotImplementedException(); |
||||
|
|
||||
|
private static void EstimateTransformOnlyDc(Span<short> residualBuffer, uint residualStride, Span<int> coefficientBuffer, uint coefficientStride, Av1TransformSize transformSize, ref ulong threeQuadEnergy, int bitDepth, Av1TransformType transformType, Av1PlaneType componentType) => throw new NotImplementedException(); |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using System.Runtime.Intrinsics; |
||||
|
using System.Runtime.Intrinsics.X86; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Implementation of a specific forward transform function.
|
||||
|
/// </summary>
|
||||
|
internal interface IAv1ForwardTransformer |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Execute the transformation.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">Input pixels.</param>
|
||||
|
/// <param name="output">Output coefficients.</param>
|
||||
|
/// <param name="cosBit">The cosinus bit.</param>
|
||||
|
/// <param name="stageRange">Stage ranges.</param>
|
||||
|
void Transform(ref int input, ref int output, int cosBit, Span<byte> stageRange); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Execute the transformation using <see cref="Avx2"/> instructions.
|
||||
|
/// </summary>
|
||||
|
/// <param name="input">Array of input vectors.</param>
|
||||
|
/// <param name="output">Array of output coefficients vectors.</param>
|
||||
|
/// <param name="cosBit">The cosinus bit.</param>
|
||||
|
/// <param name="columnNumber">The column number to process.</param>
|
||||
|
void TransformAvx2(ref Vector256<int> input, ref Vector256<int> output, int cosBit, int columnNumber); |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
internal static class InverseTransformerFactory |
||||
|
{ |
||||
|
internal static unsafe void InverseTransformAdd(ref int coefficients, Span<byte> readBuffer, int readStride, Span<byte> writeBuffer, int writeStride, Av1TransformFunctionParameters transformFunctionParameters) |
||||
|
{ |
||||
|
switch (transformFunctionParameters.TransformType) |
||||
|
{ |
||||
|
case Av1TransformType.DctDct: |
||||
|
Av1DctDctInverseTransformer.InverseTransformAdd(ref coefficients, readBuffer, readStride, writeBuffer, writeStride, transformFunctionParameters); |
||||
|
break; |
||||
|
default: |
||||
|
throw new InvalidImageContentException("Unknown transform type: " + transformFunctionParameters.TransformType); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,211 @@ |
|||||
|
// 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.Tests.Formats.Heif.Av1; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// SVY: test/FwdTxfm2dTest.cc
|
||||
|
/// </summary>
|
||||
|
[Trait("Format", "Avif")] |
||||
|
public class Av1ForwardTransformTests |
||||
|
{ |
||||
|
private static readonly double[] MaximumAllowedError = |
||||
|
[ |
||||
|
3, // 4x4 transform
|
||||
|
5, // 8x8 transform
|
||||
|
11, // 16x16 transform
|
||||
|
70, // 32x32 transform
|
||||
|
64, // 64x64 transform
|
||||
|
3.9, // 4x8 transform
|
||||
|
4.3, // 8x4 transform
|
||||
|
12, // 8x16 transform
|
||||
|
12, // 16x8 transform
|
||||
|
32, // 16x32 transform
|
||||
|
46, // 32x16 transform
|
||||
|
136, // 32x64 transform
|
||||
|
136, // 64x32 transform
|
||||
|
5, // 4x16 transform
|
||||
|
6, // 16x4 transform
|
||||
|
21, // 8x32 transform
|
||||
|
13, // 32x8 transform
|
||||
|
30, // 16x64 transform
|
||||
|
36, // 64x16 transform
|
||||
|
]; |
||||
|
|
||||
|
private readonly short[] inputOfTest; |
||||
|
private readonly int[] outputOfTest; |
||||
|
private readonly double[] inputReference; |
||||
|
private readonly double[] outputReference; |
||||
|
|
||||
|
public Av1ForwardTransformTests() |
||||
|
{ |
||||
|
this.inputOfTest = new short[64 * 64]; |
||||
|
this.outputOfTest = new int[64 * 64]; |
||||
|
this.inputReference = new double[64 * 64]; |
||||
|
this.outputReference = new double[64 * 64]; |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[MemberData(nameof(GetCombinations))] |
||||
|
public void Accuracy2dTest(int txSize, int txType, int maxAllowedError) |
||||
|
{ |
||||
|
const int bitDepth = 8; |
||||
|
Random rnd = new(0); |
||||
|
const int testBlockCount = 1; // Originally set to: 1000
|
||||
|
Av1TransformSize transformSize = (Av1TransformSize)txSize; |
||||
|
Av1TransformType transformType = (Av1TransformType)txType; |
||||
|
Av1Transform2dFlipConfiguration config = new(transformType, transformSize); |
||||
|
int width = config.TransformSize.GetWidth(); |
||||
|
int height = config.TransformSize.GetHeight(); |
||||
|
int blockSize = width * height; |
||||
|
double scaleFactor = Av1ReferenceTransform.GetScaleFactor(config, width, height); |
||||
|
|
||||
|
for (int ti = 0; ti < testBlockCount; ++ti) |
||||
|
{ |
||||
|
// prepare random test data
|
||||
|
for (int ni = 0; ni < blockSize; ++ni) |
||||
|
{ |
||||
|
this.inputOfTest[ni] = (short)rnd.Next((1 << 10) - 1); |
||||
|
this.inputReference[ni] = this.inputOfTest[ni]; |
||||
|
this.outputReference[ni] = 0; |
||||
|
this.outputOfTest[ni] = 255; |
||||
|
} |
||||
|
|
||||
|
// calculate in forward transform functions
|
||||
|
Av1ForwardTransformer.Transform2d( |
||||
|
this.inputOfTest, |
||||
|
this.outputOfTest, |
||||
|
(uint)transformSize.GetWidth(), |
||||
|
transformType, |
||||
|
transformSize, |
||||
|
bitDepth); |
||||
|
|
||||
|
// calculate in reference forward transform functions
|
||||
|
Av1ReferenceTransform.ReferenceTransformFunction2d(this.inputReference, this.outputReference, transformType, transformSize, scaleFactor); |
||||
|
|
||||
|
// repack the coefficents for some tx_size
|
||||
|
this.RepackCoefficients(width, height); |
||||
|
|
||||
|
// compare for the result is in accuracy
|
||||
|
double maximumErrorInTest = 0; |
||||
|
for (int ni = 0; ni < blockSize; ++ni) |
||||
|
{ |
||||
|
maximumErrorInTest = Math.Max(maximumErrorInTest, Math.Abs(this.outputOfTest[ni] - Math.Round(this.outputReference[ni]))); |
||||
|
} |
||||
|
|
||||
|
maximumErrorInTest /= scaleFactor; |
||||
|
Assert.True(maxAllowedError >= maximumErrorInTest, $"Forward transform 2d test with transform type: {transformType}, transform size: {transformSize} and loop: {ti}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// The max txb_width or txb_height is 32, as specified in spec 7.12.3.
|
||||
|
// Clear the high frequency coefficents and repack it in linear layout.
|
||||
|
private void RepackCoefficients(int tx_width, int tx_height) |
||||
|
{ |
||||
|
for (int i = 0; i < 2; ++i) |
||||
|
{ |
||||
|
uint e_size = i == 0 ? (uint)sizeof(int) : sizeof(double); |
||||
|
ref byte output = ref (i == 0) ? ref Unsafe.As<int, byte>(ref this.outputOfTest[0]) |
||||
|
: ref Unsafe.As<double, byte>(ref this.outputReference[0]); |
||||
|
|
||||
|
if (tx_width == 64 && tx_height == 64) |
||||
|
{ |
||||
|
// tx_size == TX_64X64
|
||||
|
// zero out top-right 32x32 area.
|
||||
|
for (uint row = 0; row < 32; ++row) |
||||
|
{ |
||||
|
Unsafe.InitBlock(ref Unsafe.Add(ref output, ((row * 64) + 32) * e_size), 0, 32 * e_size); |
||||
|
} |
||||
|
|
||||
|
// zero out the bottom 64x32 area.
|
||||
|
Unsafe.InitBlock(ref Unsafe.Add(ref output, 32 * 64 * e_size), 0, 32 * 64 * e_size); |
||||
|
|
||||
|
// Re-pack non-zero coeffs in the first 32x32 indices.
|
||||
|
for (uint row = 1; row < 32; ++row) |
||||
|
{ |
||||
|
Unsafe.CopyBlock( |
||||
|
ref Unsafe.Add(ref output, row * 32 * e_size), |
||||
|
ref Unsafe.Add(ref output, row * 64 * e_size), |
||||
|
32 * e_size); |
||||
|
} |
||||
|
} |
||||
|
else if (tx_width == 32 && tx_height == 64) |
||||
|
{ |
||||
|
// tx_size == TX_32X64
|
||||
|
// zero out the bottom 32x32 area.
|
||||
|
Unsafe.InitBlock(ref Unsafe.Add(ref output, 32 * 32 * e_size), 0, 32 * 32 * e_size); |
||||
|
|
||||
|
// Note: no repacking needed here.
|
||||
|
} |
||||
|
else if (tx_width == 64 && tx_height == 32) |
||||
|
{ |
||||
|
// tx_size == TX_64X32
|
||||
|
// zero out right 32x32 area.
|
||||
|
for (uint row = 0; row < 32; ++row) |
||||
|
{ |
||||
|
Unsafe.InitBlock(ref Unsafe.Add(ref output, ((row * 64) + 32) * e_size), 0, 32 * e_size); |
||||
|
} |
||||
|
|
||||
|
// Re-pack non-zero coeffs in the first 32x32 indices.
|
||||
|
for (uint row = 1; row < 32; ++row) |
||||
|
{ |
||||
|
Unsafe.CopyBlock( |
||||
|
ref Unsafe.Add(ref output, row * 32 * e_size), |
||||
|
ref Unsafe.Add(ref output, row * 64 * e_size), |
||||
|
32 * e_size); |
||||
|
} |
||||
|
} |
||||
|
else if (tx_width == 16 && tx_height == 64) |
||||
|
{ |
||||
|
// tx_size == TX_16X64
|
||||
|
// zero out the bottom 16x32 area.
|
||||
|
Unsafe.InitBlock(ref Unsafe.Add(ref output, 16 * 32 * e_size), 0, 16 * 32 * e_size); |
||||
|
|
||||
|
// Note: no repacking needed here.
|
||||
|
} |
||||
|
else if (tx_width == 64 && |
||||
|
tx_height == 16) |
||||
|
{ |
||||
|
// tx_size == TX_64X16
|
||||
|
// zero out right 32x16 area.
|
||||
|
for (uint row = 0; row < 16; ++row) |
||||
|
{ |
||||
|
Unsafe.InitBlock(ref Unsafe.Add(ref output, ((row * 64) + 32) * e_size), 0, 32 * e_size); |
||||
|
} |
||||
|
|
||||
|
// Re-pack non-zero coeffs in the first 32x16 indices.
|
||||
|
for (uint row = 1; row < 16; ++row) |
||||
|
{ |
||||
|
Unsafe.CopyBlock( |
||||
|
ref Unsafe.Add(ref output, row * 32 * e_size), |
||||
|
ref Unsafe.Add(ref output, row * 64 * e_size), |
||||
|
32 * e_size); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static TheoryData<int, int, int> GetCombinations() |
||||
|
{ |
||||
|
TheoryData<int, int, int> combinations = []; |
||||
|
for (int s = 0; s < (int)Av1TransformSize.AllSizes; s++) |
||||
|
{ |
||||
|
double maxError = MaximumAllowedError[s]; |
||||
|
for (int t = 0; t < (int)Av1TransformType.AllTransformTypes; ++t) |
||||
|
{ |
||||
|
Av1TransformType transformType = (Av1TransformType)t; |
||||
|
Av1TransformSize transformSize = (Av1TransformSize)s; |
||||
|
Av1Transform2dFlipConfiguration config = new(transformType, transformSize); |
||||
|
if (config.IsAllowed()) |
||||
|
{ |
||||
|
combinations.Add(s, t, (int)maxError); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return combinations; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,273 @@ |
|||||
|
// Copyright (c) Six Labors.
|
||||
|
// Licensed under the Six Labors Split License.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Heif.Av1; |
||||
|
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; |
||||
|
|
||||
|
internal class Av1ReferenceTransform |
||||
|
{ |
||||
|
/****************************************************************************** |
||||
|
* SVT file: test/ref/TxfmRef.cc |
||||
|
* |
||||
|
* Reference implementation for txfm, including : |
||||
|
* - reference_dct_1d |
||||
|
* - reference_adst_1d |
||||
|
* - reference_idtx_1d |
||||
|
* - reference_txfm_1d |
||||
|
* - reference_txfm_2d |
||||
|
* - fadst_ref |
||||
|
* |
||||
|
* Original authors: Cidana-Edmond, Cidana-Wenyao |
||||
|
* |
||||
|
******************************************************************************/ |
||||
|
|
||||
|
public static double GetScaleFactor(Av1Transform2dFlipConfiguration config, int transformWidth, int transformHeight) |
||||
|
{ |
||||
|
Span<int> shift = config.Shift; |
||||
|
int amplifyBit = shift[0] + shift[1] + shift[2]; |
||||
|
double scaleFactor = |
||||
|
amplifyBit >= 0 ? (1 << amplifyBit) : (1.0 / (1 << -amplifyBit)); |
||||
|
|
||||
|
// For rectangular transforms, we need to multiply by an extra factor.
|
||||
|
int rectType = Av1ForwardTransformer.GetRectangularRatio(transformWidth, transformHeight); |
||||
|
if (Math.Abs(rectType) == 1) |
||||
|
{ |
||||
|
scaleFactor *= Math.Pow(2, 0.5); |
||||
|
} |
||||
|
|
||||
|
return scaleFactor; |
||||
|
} |
||||
|
|
||||
|
public static void ReferenceTransformFunction2d(Span<double> input, Span<double> output, Av1TransformType transformType, Av1TransformSize transformSize, double scaleFactor) |
||||
|
{ |
||||
|
// Get transform type and size of each dimension.
|
||||
|
Av1Transform2dFlipConfiguration config = new(transformType, transformSize); |
||||
|
Av1TransformType1d columnType = GetTransformType1d(config.TransformFunctionTypeColumn); |
||||
|
Av1TransformType1d rowType = GetTransformType1d(config.TransformFunctionTypeRow); |
||||
|
int transformWidth = transformSize.GetWidth(); |
||||
|
int transformHeight = transformSize.GetHeight(); |
||||
|
Span<double> tmpInput = new double[transformWidth * transformHeight]; |
||||
|
Span<double> tmpOutput = new double[transformWidth * transformHeight]; |
||||
|
|
||||
|
// second forward transform with row_type
|
||||
|
for (int r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
ReferenceTransform1d(rowType, input[(r * transformWidth)..], output[(r * transformWidth)..], transformWidth); |
||||
|
} |
||||
|
|
||||
|
// matrix transposition
|
||||
|
for (int r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
for (int c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
tmpInput[(c * transformHeight) + r] = output[(r * transformWidth) + c]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// first forward transform with column_type
|
||||
|
for (int c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
ReferenceTransform1d( |
||||
|
columnType, |
||||
|
tmpInput[(c * transformHeight)..], |
||||
|
tmpOutput[(c * transformHeight)..], |
||||
|
transformHeight); |
||||
|
} |
||||
|
|
||||
|
// matrix transposition
|
||||
|
for (int r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
for (int c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
output[(c * transformHeight) + r] = tmpOutput[(r * transformWidth) + c]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// appropriate scale
|
||||
|
for (int r = 0; r < transformHeight; ++r) |
||||
|
{ |
||||
|
for (int c = 0; c < transformWidth; ++c) |
||||
|
{ |
||||
|
output[(r * transformWidth) + c] *= scaleFactor; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void Adst4Reference(Span<int> input, Span<int> output) |
||||
|
{ |
||||
|
// 16384 * sqrt(2) * sin(kPi/9) * 2 / 3
|
||||
|
const long sinPi19 = 5283; |
||||
|
const long sinPi29 = 9929; |
||||
|
const long sinPi39 = 13377; |
||||
|
const long sinPi49 = 15212; |
||||
|
|
||||
|
long x0, x1, x2, x3; |
||||
|
long s0, s1, s2, s3, s4, s5, s6, s7; |
||||
|
x0 = input[0]; |
||||
|
x1 = input[1]; |
||||
|
x2 = input[2]; |
||||
|
x3 = input[3]; |
||||
|
|
||||
|
if ((x0 | x1 | x2 | x3) == 0L) |
||||
|
{ |
||||
|
output[0] = output[1] = output[2] = output[3] = 0; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
s0 = sinPi19 * x0; |
||||
|
s1 = sinPi49 * x0; |
||||
|
s2 = sinPi29 * x1; |
||||
|
s3 = sinPi19 * x1; |
||||
|
s4 = sinPi39 * x2; |
||||
|
s5 = sinPi49 * x3; |
||||
|
s6 = sinPi29 * x3; |
||||
|
s7 = x0 + x1 - x3; |
||||
|
|
||||
|
x0 = s0 + s2 + s5; |
||||
|
x1 = sinPi39 * s7; |
||||
|
x2 = s1 - s3 + s6; |
||||
|
x3 = s4; |
||||
|
|
||||
|
s0 = x0 + x3; |
||||
|
s1 = x1; |
||||
|
s2 = x2 - x3; |
||||
|
s3 = x2 - x0 + x3; |
||||
|
|
||||
|
// 1-D transform scaling factor is sqrt(2).
|
||||
|
output[0] = Av1Math.RoundShift(s0, 14); |
||||
|
output[1] = Av1Math.RoundShift(s1, 14); |
||||
|
output[2] = Av1Math.RoundShift(s2, 14); |
||||
|
output[3] = Av1Math.RoundShift(s3, 14); |
||||
|
} |
||||
|
|
||||
|
private static void ReferenceIdentity1d(Span<double> input, Span<double> output, int size) |
||||
|
{ |
||||
|
const double sqrt2 = 1.4142135623730950488016887242097f; |
||||
|
double scale = 0; |
||||
|
switch (size) |
||||
|
{ |
||||
|
case 4: |
||||
|
scale = sqrt2; |
||||
|
break; |
||||
|
case 8: |
||||
|
scale = 2; |
||||
|
break; |
||||
|
case 16: |
||||
|
scale = 2 * sqrt2; |
||||
|
break; |
||||
|
case 32: |
||||
|
scale = 4; |
||||
|
break; |
||||
|
case 64: |
||||
|
scale = 4 * sqrt2; |
||||
|
break; |
||||
|
default: |
||||
|
Assert.Fail(); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
for (int k = 0; k < size; ++k) |
||||
|
{ |
||||
|
output[k] = input[k] * scale; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void ReferenceDct1d(Span<double> input, Span<double> output, int size) |
||||
|
{ |
||||
|
const double kInvSqrt2 = 0.707106781186547524400844362104f; |
||||
|
for (int k = 0; k < size; ++k) |
||||
|
{ |
||||
|
output[k] = 0; |
||||
|
for (int n = 0; n < size; ++n) |
||||
|
{ |
||||
|
output[k] += input[n] * Math.Cos(Math.PI * ((2 * n) + 1) * k / (2 * size)); |
||||
|
} |
||||
|
|
||||
|
if (k == 0) |
||||
|
{ |
||||
|
output[k] = output[k] * kInvSqrt2; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void ReferenceAdst1d(Span<double> input, Span<double> output, int size) |
||||
|
{ |
||||
|
if (size == 4) |
||||
|
{ |
||||
|
// Special case.
|
||||
|
int[] int_input = new int[4]; |
||||
|
for (int i = 0; i < 4; ++i) |
||||
|
{ |
||||
|
int_input[i] = (int)Math.Round(input[i]); |
||||
|
} |
||||
|
|
||||
|
int[] int_output = new int[4]; |
||||
|
Adst4Reference(int_input, int_output); |
||||
|
for (int i = 0; i < 4; ++i) |
||||
|
{ |
||||
|
output[i] = int_output[i]; |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
for (int k = 0; k < size; ++k) |
||||
|
{ |
||||
|
output[k] = 0; |
||||
|
for (int n = 0; n < size; ++n) |
||||
|
{ |
||||
|
output[k] += input[n] * Math.Sin(Math.PI * ((2 * n) + 1) * ((2 * k) + 1) / (4 * size)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static void ReferenceTransform1d(Av1TransformType1d type, Span<double> input, Span<double> output, int size) |
||||
|
{ |
||||
|
switch (type) |
||||
|
{ |
||||
|
case Av1TransformType1d.Dct: |
||||
|
ReferenceDct1d(input, output, size); |
||||
|
break; |
||||
|
case Av1TransformType1d.Adst: |
||||
|
case Av1TransformType1d.FlipAdst: |
||||
|
ReferenceAdst1d(input, output, size); |
||||
|
break; |
||||
|
case Av1TransformType1d.Identity: |
||||
|
ReferenceIdentity1d(input, output, size); |
||||
|
break; |
||||
|
default: |
||||
|
Assert.Fail(); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static Av1TransformType1d GetTransformType1d(Av1TransformFunctionType transformFunctionType) |
||||
|
{ |
||||
|
switch (transformFunctionType) |
||||
|
{ |
||||
|
case Av1TransformFunctionType.Dct4: |
||||
|
case Av1TransformFunctionType.Dct8: |
||||
|
case Av1TransformFunctionType.Dct16: |
||||
|
case Av1TransformFunctionType.Dct32: |
||||
|
case Av1TransformFunctionType.Dct64: |
||||
|
return Av1TransformType1d.Dct; |
||||
|
case Av1TransformFunctionType.Adst4: |
||||
|
case Av1TransformFunctionType.Adst8: |
||||
|
case Av1TransformFunctionType.Adst16: |
||||
|
case Av1TransformFunctionType.Adst32: |
||||
|
return Av1TransformType1d.Adst; |
||||
|
case Av1TransformFunctionType.Identity4: |
||||
|
case Av1TransformFunctionType.Identity8: |
||||
|
case Av1TransformFunctionType.Identity16: |
||||
|
case Av1TransformFunctionType.Identity32: |
||||
|
case Av1TransformFunctionType.Identity64: |
||||
|
return Av1TransformType1d.Identity; |
||||
|
case Av1TransformFunctionType.Invalid: |
||||
|
default: |
||||
|
Assert.Fail(); |
||||
|
return (Av1TransformType1d)5; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue