Browse Source

Skeleton code for Transform

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
ad57a99ff1
  1. 8
      src/ImageSharp/Formats/Heif/Av1/Av1Math.cs
  2. 12
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1CoefficientShape.cs
  3. 157
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1DctDctInverseTransformer.cs
  4. 276
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs
  5. 41
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.cs
  6. 75
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1SinusConstants.cs
  7. 313
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs
  8. 19
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformFunctionParameters.cs
  9. 23
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformFunctionType.cs
  10. 12
      src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformType1d.cs
  11. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst16ForwardTransformer.cs
  12. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst32ForwardTransformer.cs
  13. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst4ForwardTransformer.cs
  14. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst8ForwardTransformer.cs
  15. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct16ForwardTransformer.cs
  16. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct32ForwardTransformer.cs
  17. 85
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct4ForwardTransformer.cs
  18. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct64ForwardTransformer.cs
  19. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct8ForwardTransformer.cs
  20. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity16ForwardTransformer.cs
  21. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity32ForwardTransformer.cs
  22. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity4ForwardTransformer.cs
  23. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity64ForwardTransformer.cs
  24. 15
      src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity8ForwardTransformer.cs
  25. 56
      src/ImageSharp/Formats/Heif/Av1/Transform/ForwardTransformerFactory.cs
  26. 31
      src/ImageSharp/Formats/Heif/Av1/Transform/IAv1ForwardTransformer.cs
  27. 19
      src/ImageSharp/Formats/Heif/Av1/Transform/InverseTransformerFactory.cs
  28. 211
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ForwardTransformTests.cs
  29. 273
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReferenceTransform.cs

8
src/ImageSharp/Formats/Heif/Av1/Av1Math.cs

@ -1,6 +1,8 @@
// Copyright (c) Six Labors. // Copyright (c) Six Labors.
// Licensed under the Six Labors Split License. // Licensed under the Six Labors Split License.
using System;
namespace SixLabors.ImageSharp.Formats.Heif.Av1; namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal static class Av1Math internal static class Av1Math
@ -155,4 +157,10 @@ internal static class Av1Math
internal static int RoundPowerOf2Signed(int value, int n) internal static int RoundPowerOf2Signed(int value, int n)
=> (value < 0) ? -RoundPowerOf2(-value, n) : RoundPowerOf2(value, n); => (value < 0) ? -RoundPowerOf2(-value, n) : RoundPowerOf2(value, n);
internal static int RoundShift(long value, int bit)
{
DebugGuard.MustBeGreaterThanOrEqualTo(bit, 1, nameof(bit));
return (int)((value + (1L << (bit - 1))) >> bit);
}
} }

12
src/ImageSharp/Formats/Heif/Av1/Transform/Av1CoefficientShape.cs

@ -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
}

157
src/ImageSharp/Formats/Heif/Av1/Transform/Av1DctDctInverseTransformer.cs

@ -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),
};
}

276
src/ImageSharp/Formats/Heif/Av1/Transform/Av1ForwardTransformer.cs

@ -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
}
}

41
src/ImageSharp/Formats/Heif/Av1/Transform/Av1InverseTransformer.cs

@ -8,8 +8,45 @@ internal class Av1InverseTransformer
/// <summary> /// <summary>
/// SVT: svt_aom_inv_transform_recon8bit /// SVT: svt_aom_inv_transform_recon8bit
/// </summary> /// </summary>
public static void Reconstruct8Bit(Span<int> coefficientsBuffer, Span<byte> transformBlockReconstructionBuffer1, int reconstructionStride1, Span<byte> transformBlockReconstructionBuffer2, int reconstructionStride2, Av1TransformSize transformSize, Av1TransformType transformType, int plane, int numberOfCoefficients, bool isLossless) public static void Reconstruct8Bit(Span<int> coefficientsBuffer, Span<byte> reconstructionBufferRead, int reconstructionReadStride, Span<byte> reconstructionBufferWrite, int reconstructionWriteStride, Av1TransformSize transformSize, Av1TransformType transformType, int plane, int numberOfCoefficients, bool isLossless)
{ {
throw new NotImplementedException("Inverse transformation not implemented yet."); Av1TransformFunctionParameters transformFunctionParameters = new()
{
TransformType = transformType,
TransformSize = transformSize,
EndOfBuffer = numberOfCoefficients,
IsLossless = isLossless,
BitDepth = 8,
Is16BitPipeline = false
};
if (reconstructionBufferRead != reconstructionBufferWrite)
{
/* When output pointers to read and write are differents,
* then kernel copy also all buffer from read to write,
* and cannot be limited by End Of Buffer calculations. */
transformFunctionParameters.EndOfBuffer = GetMaxEndOfBuffer(transformSize);
}
InverseTransformerFactory.InverseTransformAdd(
ref coefficientsBuffer[0], reconstructionBufferRead, reconstructionReadStride, reconstructionBufferWrite, reconstructionWriteStride, transformFunctionParameters);
}
/// <summary>
/// SVT: av1_get_max_eob
/// </summary>
private static int GetMaxEndOfBuffer(Av1TransformSize transformSize)
{
if (transformSize is Av1TransformSize.Size64x64 or Av1TransformSize.Size64x32 or Av1TransformSize.Size32x64)
{
return 1024;
}
if (transformSize is Av1TransformSize.Size16x64 or Av1TransformSize.Size64x16)
{
return 512;
}
return transformSize.GetSize2d();
} }
} }

75
src/ImageSharp/Formats/Heif/Av1/Transform/Av1SinusConstants.cs

@ -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];
}

313
src/ImageSharp/Formats/Heif/Av1/Transform/Av1Transform2dFlipConfiguration.cs

@ -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;
}
}
}
}

19
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformFunctionParameters.cs

@ -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; }
}

23
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformFunctionType.cs

@ -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,
}

12
src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformType1d.cs

@ -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
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst16ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst32ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst4ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Adst8ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct16ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct32ForwardTransformer.cs

@ -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();
}

85
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct4ForwardTransformer.cs

@ -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();
*/
}
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct64ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Dct8ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity16ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity32ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity4ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity64ForwardTransformer.cs

@ -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();
}

15
src/ImageSharp/Formats/Heif/Av1/Transform/Forward/Av1Identity8ForwardTransformer.cs

@ -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();
}

56
src/ImageSharp/Formats/Heif/Av1/Transform/ForwardTransformerFactory.cs

@ -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();
}

31
src/ImageSharp/Formats/Heif/Av1/Transform/IAv1ForwardTransformer.cs

@ -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);
}

19
src/ImageSharp/Formats/Heif/Av1/Transform/InverseTransformerFactory.cs

@ -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);
}
}
}

211
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ForwardTransformTests.cs

@ -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;
}
}

273
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1ReferenceTransform.cs

@ -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…
Cancel
Save