mirror of https://github.com/SixLabors/ImageSharp
10 changed files with 554 additions and 0 deletions
@ -0,0 +1,78 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
internal class Av1Adst4Inverse1dTransformer : IAv1Forward1dTransformer |
|||
{ |
|||
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange) |
|||
{ |
|||
Guard.MustBeSizedAtLeast(input, 4, nameof(input)); |
|||
Guard.MustBeSizedAtLeast(output, 4, nameof(output)); |
|||
TransformScalar(ref input[0], ref output[0], cosBit, stageRange); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_iadst4_new
|
|||
/// </summary>
|
|||
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
|||
{ |
|||
int bit = cosBit; |
|||
Span<int> sinpi = Av1SinusConstants.SinusPi(bit); |
|||
int s0, s1, s2, s3, s4, s5, s6, s7; |
|||
|
|||
int x0 = input; |
|||
int x1 = Unsafe.Add(ref input, 1); |
|||
int x2 = Unsafe.Add(ref input, 2); |
|||
int x3 = Unsafe.Add(ref input, 3); |
|||
|
|||
if (!(x0 != 0 | x1 != 0 | x2 != 0 | x3 != 0)) |
|||
{ |
|||
output = 0; |
|||
Unsafe.Add(ref output, 1) = 0; |
|||
Unsafe.Add(ref output, 2) = 0; |
|||
Unsafe.Add(ref output, 3) = 0; |
|||
return; |
|||
} |
|||
|
|||
Guard.IsTrue(sinpi[1] + sinpi[2] == sinpi[4], nameof(sinpi), "Sinus Pi check failed."); |
|||
|
|||
s0 = sinpi[1] * x0; |
|||
s1 = sinpi[2] * x0; |
|||
s2 = sinpi[3] * x1; |
|||
s3 = sinpi[4] * x2; |
|||
s4 = sinpi[1] * x2; |
|||
s5 = sinpi[2] * x3; |
|||
s6 = sinpi[4] * x3; |
|||
|
|||
s7 = (x0 - x2) + x3; |
|||
|
|||
// stage 3
|
|||
s0 = s0 + s3; |
|||
s1 = s1 - s4; |
|||
s3 = s2; |
|||
s2 = sinpi[3] * s7; |
|||
|
|||
// stage 4
|
|||
s0 = s0 + s5; |
|||
s1 = s1 - s6; |
|||
|
|||
// stage 5
|
|||
x0 = s0 + s3; |
|||
x1 = s1 + s3; |
|||
x2 = s2; |
|||
x3 = s0 + s1; |
|||
|
|||
// stage 6
|
|||
x3 = x3 - s3; |
|||
|
|||
output = Av1Math.RoundShift(x0, bit); |
|||
Unsafe.Add(ref output, 1) = Av1Math.RoundShift(x1, bit); |
|||
Unsafe.Add(ref output, 2) = Av1Math.RoundShift(x2, bit); |
|||
Unsafe.Add(ref output, 3) = Av1Math.RoundShift(x3, bit); |
|||
|
|||
// range_check_buf(6, input, output, 4, stage_range[6]);
|
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
internal class Av1Dct4Inverse1dTransformer : IAv1Forward1dTransformer |
|||
{ |
|||
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange) |
|||
{ |
|||
Guard.MustBeSizedAtLeast(input, 4, nameof(input)); |
|||
Guard.MustBeSizedAtLeast(output, 4, nameof(output)); |
|||
TransformScalar(ref input[0], ref output[0], cosBit, stageRange); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_idct4_new
|
|||
/// </summary>
|
|||
private static void TransformScalar(ref int input, ref int output, int cosBit, Span<byte> stageRange) |
|||
{ |
|||
Span<int> cospi = Av1SinusConstants.CosinusPi(cosBit); |
|||
int stage = 0; |
|||
Span<int> temp0 = stackalloc int[4]; |
|||
Span<int> temp1 = stackalloc int[4]; |
|||
|
|||
// stage 0;
|
|||
|
|||
// stage 1;
|
|||
stage++; |
|||
temp0[0] = input; |
|||
temp0[1] = Unsafe.Add(ref input, 2); |
|||
temp0[2] = Unsafe.Add(ref input, 1); |
|||
temp0[3] = Unsafe.Add(ref input, 3); |
|||
|
|||
// range_check_buf(stage, input, bf1, size, stage_range[stage]);
|
|||
|
|||
// stage 2
|
|||
stage++; |
|||
temp1[0] = HalfButterfly(cospi[32], temp0[0], cospi[32], temp0[1], cosBit); |
|||
temp1[1] = HalfButterfly(cospi[32], temp0[0], -cospi[32], temp0[1], cosBit); |
|||
temp1[2] = HalfButterfly(cospi[48], temp0[2], -cospi[16], temp0[3], cosBit); |
|||
temp1[3] = HalfButterfly(cospi[16], temp0[2], cospi[48], temp0[3], cosBit); |
|||
|
|||
// range_check_buf(stage, input, bf1, size, stage_range[stage]);
|
|||
|
|||
// stage 3
|
|||
stage++; |
|||
Unsafe.Add(ref output, 0) = ClampValue(temp1[0] + temp1[3], stageRange[stage]); |
|||
Unsafe.Add(ref output, 1) = ClampValue(temp1[1] + temp1[2], stageRange[stage]); |
|||
Unsafe.Add(ref output, 2) = ClampValue(temp1[1] - temp1[2], stageRange[stage]); |
|||
Unsafe.Add(ref output, 3) = ClampValue(temp1[0] - temp1[3], stageRange[stage]); |
|||
} |
|||
|
|||
internal static int ClampValue(int value, byte bit) |
|||
{ |
|||
if (bit <= 0) |
|||
{ |
|||
return value; // Do nothing for invalid clamp bit.
|
|||
} |
|||
|
|||
long max_value = (1L << (bit - 1)) - 1; |
|||
long min_value = -(1L << (bit - 1)); |
|||
return (int)Av1Math.Clamp(value, min_value, max_value); |
|||
} |
|||
|
|||
internal static int HalfButterfly(int w0, int in0, int w1, int in1, int bit) |
|||
{ |
|||
long result64 = (long)(w0 * in0) + (w1 * in1); |
|||
long intermediate = result64 + (1L << (bit - 1)); |
|||
|
|||
// NOTE(david.barker): The value 'result_64' may not necessarily fit
|
|||
// into 32 bits. However, the result of this function is nominally
|
|||
// ROUND_POWER_OF_TWO_64(result_64, bit)
|
|||
// and that is required to fit into stage_range[stage] many bits
|
|||
// (checked by range_check_buf()).
|
|||
//
|
|||
// Here we've unpacked that rounding operation, and it can be shown
|
|||
// that the value of 'intermediate' here *does* fit into 32 bits
|
|||
// for any conformant bitstream.
|
|||
// The upshot is that, if you do all this calculation using
|
|||
// wrapping 32-bit arithmetic instead of (non-wrapping) 64-bit arithmetic,
|
|||
// then you'll still get the correct result.
|
|||
return (int)(intermediate >> bit); |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
internal class Av1Identity16Inverse1dTransformer : IAv1Forward1dTransformer |
|||
{ |
|||
private const long Sqrt2Times2 = Av1Identity4Inverse1dTransformer.Sqrt2 >> 1; |
|||
|
|||
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange) |
|||
{ |
|||
Guard.MustBeSizedAtLeast(input, 16, nameof(input)); |
|||
Guard.MustBeSizedAtLeast(output, 16, nameof(output)); |
|||
TransformScalar(ref input[0], ref output[0]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_iidentity16_c
|
|||
/// </summary>
|
|||
private static void TransformScalar(ref int input, ref int output) |
|||
{ |
|||
// Normal input should fit into 32-bit. Cast to 64-bit here to avoid
|
|||
// overflow with corrupted/fuzzed input. The same for av1_iidentity/16/64_c.
|
|||
output = Av1Math.RoundShift(Sqrt2Times2 * input, Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 1) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 1), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 2) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 2), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 3) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 3), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 4) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 4), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 5) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 5), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 6) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 6), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 7) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 7), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 8) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 8), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 9) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 9), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 10) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 10), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 11) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 11), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 12) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 12), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 13) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 13), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 14) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 14), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 15) = Av1Math.RoundShift(Sqrt2Times2 * Unsafe.Add(ref input, 15), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
internal class Av1Identity32Inverse1dTransformer : IAv1Forward1dTransformer |
|||
{ |
|||
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange) |
|||
{ |
|||
Guard.MustBeSizedAtLeast(input, 32, nameof(input)); |
|||
Guard.MustBeSizedAtLeast(output, 32, nameof(output)); |
|||
ref int inputRef = ref input[0]; |
|||
ref int outputRef = ref output[0]; |
|||
TransformScalar(ref inputRef, ref outputRef); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 8), ref Unsafe.Add(ref outputRef, 8)); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 16), ref Unsafe.Add(ref outputRef, 16)); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 24), ref Unsafe.Add(ref outputRef, 24)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_iidentity32_c
|
|||
/// </summary>
|
|||
private static void TransformScalar(ref int input, ref int output) |
|||
{ |
|||
output = input << 2; |
|||
Unsafe.Add(ref output, 1) = Unsafe.Add(ref input, 1) << 2; |
|||
Unsafe.Add(ref output, 2) = Unsafe.Add(ref input, 2) << 2; |
|||
Unsafe.Add(ref output, 3) = Unsafe.Add(ref input, 3) << 2; |
|||
Unsafe.Add(ref output, 4) = Unsafe.Add(ref input, 4) << 2; |
|||
Unsafe.Add(ref output, 5) = Unsafe.Add(ref input, 5) << 2; |
|||
Unsafe.Add(ref output, 6) = Unsafe.Add(ref input, 6) << 2; |
|||
Unsafe.Add(ref output, 7) = Unsafe.Add(ref input, 7) << 2; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
internal class Av1Identity4Inverse1dTransformer : IAv1Forward1dTransformer |
|||
{ |
|||
internal const int Sqrt2Bits = 12; |
|||
|
|||
// 2^12 * sqrt(2)
|
|||
internal const long Sqrt2 = 5793; |
|||
|
|||
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange) |
|||
{ |
|||
Guard.MustBeSizedAtLeast(input, 4, nameof(input)); |
|||
Guard.MustBeSizedAtLeast(output, 4, nameof(output)); |
|||
TransformScalar(ref input[0], ref output[0]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_iidentity4_c
|
|||
/// </summary>
|
|||
private static void TransformScalar(ref int input, ref int output) |
|||
{ |
|||
// Normal input should fit into 32-bit. Cast to 64-bit here to avoid
|
|||
// overflow with corrupted/fuzzed input. The same for av1_iidentity/16/64_c.
|
|||
output = Av1Math.RoundShift(Sqrt2 * input, Sqrt2Bits); |
|||
Unsafe.Add(ref output, 1) = Av1Math.RoundShift(Sqrt2 * Unsafe.Add(ref input, 1), Sqrt2Bits); |
|||
Unsafe.Add(ref output, 2) = Av1Math.RoundShift(Sqrt2 * Unsafe.Add(ref input, 2), Sqrt2Bits); |
|||
Unsafe.Add(ref output, 3) = Av1Math.RoundShift(Sqrt2 * Unsafe.Add(ref input, 3), Sqrt2Bits); |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
internal class Av1Identity64Inverse1dTransformer : IAv1Forward1dTransformer |
|||
{ |
|||
private const long Sqrt2Times4 = Av1Identity4Inverse1dTransformer.Sqrt2 >> 2; |
|||
|
|||
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange) |
|||
{ |
|||
Guard.MustBeSizedAtLeast(input, 64, nameof(input)); |
|||
Guard.MustBeSizedAtLeast(output, 64, nameof(output)); |
|||
ref int inputRef = ref input[0]; |
|||
ref int outputRef = ref output[0]; |
|||
TransformScalar(ref inputRef, ref outputRef); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 8), ref Unsafe.Add(ref outputRef, 8)); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 16), ref Unsafe.Add(ref outputRef, 16)); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 24), ref Unsafe.Add(ref outputRef, 24)); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 32), ref Unsafe.Add(ref outputRef, 32)); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 40), ref Unsafe.Add(ref outputRef, 40)); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 48), ref Unsafe.Add(ref outputRef, 48)); |
|||
TransformScalar(ref Unsafe.Add(ref inputRef, 56), ref Unsafe.Add(ref outputRef, 56)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_iidentity64_c
|
|||
/// </summary>
|
|||
private static void TransformScalar(ref int input, ref int output) |
|||
{ |
|||
// Normal input should fit into 32-bit. Cast to 64-bit here to avoid
|
|||
// overflow with corrupted/fuzzed input. The same for av1_iidentity/16/64_c.
|
|||
output = Av1Math.RoundShift(Sqrt2Times4 * input, Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 1) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 1), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 2) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 2), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 3) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 3), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 4) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 4), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 5) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 5), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 6) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 6), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
Unsafe.Add(ref output, 7) = Av1Math.RoundShift(Sqrt2Times4 * Unsafe.Add(ref input, 7), Av1Identity4Inverse1dTransformer.Sqrt2Bits); |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
internal class Av1Identity8Inverse1dTransformer : IAv1Forward1dTransformer |
|||
{ |
|||
public void Transform(Span<int> input, Span<int> output, int cosBit, Span<byte> stageRange) |
|||
{ |
|||
Guard.MustBeSizedAtLeast(input, 8, nameof(input)); |
|||
Guard.MustBeSizedAtLeast(output, 8, nameof(output)); |
|||
TransformScalar(ref input[0], ref output[0]); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// SVT: svt_av1_iidentity8_c
|
|||
/// </summary>
|
|||
private static void TransformScalar(ref int input, ref int output) |
|||
{ |
|||
output = input << 1; |
|||
Unsafe.Add(ref output, 1) = Unsafe.Add(ref input, 1) << 1; |
|||
Unsafe.Add(ref output, 2) = Unsafe.Add(ref input, 2) << 1; |
|||
Unsafe.Add(ref output, 3) = Unsafe.Add(ref input, 3) << 1; |
|||
Unsafe.Add(ref output, 4) = Unsafe.Add(ref input, 4) << 1; |
|||
Unsafe.Add(ref output, 5) = Unsafe.Add(ref input, 5) << 1; |
|||
Unsafe.Add(ref output, 6) = Unsafe.Add(ref input, 6) << 1; |
|||
Unsafe.Add(ref output, 7) = Unsafe.Add(ref input, 7) << 1; |
|||
} |
|||
} |
|||
@ -0,0 +1,196 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Six Labors Split License.
|
|||
|
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Forward; |
|||
using SixLabors.ImageSharp.Formats.Heif.Av1.Transform.Inverse; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; |
|||
|
|||
/// <summary>
|
|||
/// SVT: test/InvTxfm1dTest.cc
|
|||
/// SVT: test/InvTxfm2dAsmTest.cc
|
|||
/// </summary>
|
|||
[Trait("Format", "Avif")] |
|||
public class Av1InverseTransformTests |
|||
{ |
|||
[Fact] |
|||
public void AccuracyOfDct1dTransformSize4Test() |
|||
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size4x4, 1); |
|||
|
|||
// [Fact]
|
|||
public void AccuracyOfDct1dTransformSize8Test() |
|||
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size8x8, 1, 2); |
|||
|
|||
// [Fact]
|
|||
public void AccuracyOfDct1dTransformSize16Test() |
|||
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size16x16, 1, 3); |
|||
|
|||
// [Fact]
|
|||
public void AccuracyOfDct1dTransformSize32Test() |
|||
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size32x32, 1, 4); |
|||
|
|||
// [Fact]
|
|||
public void AccuracyOfDct1dTransformSize64Test() |
|||
=> AssertAccuracy1d(Av1TransformType.DctDct, Av1TransformSize.Size64x64, 1, 5); |
|||
|
|||
[Fact] |
|||
public void AccuracyOfAdst1dTransformSize4Test() |
|||
=> AssertAccuracy1d(Av1TransformType.AdstAdst, Av1TransformSize.Size4x4, 1); |
|||
|
|||
// [Fact]
|
|||
public void AccuracyOfAdst1dTransformSize8Test() |
|||
=> AssertAccuracy1d(Av1TransformType.AdstAdst, Av1TransformSize.Size8x8, 1, 2); |
|||
|
|||
// [Fact]
|
|||
public void AccuracyOfAdst1dTransformSize16Test() |
|||
=> AssertAccuracy1d(Av1TransformType.AdstAdst, Av1TransformSize.Size16x16, 1, 3); |
|||
|
|||
// [Fact]
|
|||
public void AccuracyOfAdst1dTransformSize32Test() |
|||
=> AssertAccuracy1d(Av1TransformType.AdstAdst, Av1TransformSize.Size32x32, 1, 3); |
|||
|
|||
[Fact] |
|||
public void AccuracyOfIdentity1dTransformSize4Test() |
|||
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size4x4, 1); |
|||
|
|||
[Fact] |
|||
public void AccuracyOfIdentity1dTransformSize8Test() |
|||
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size8x8, 2); |
|||
|
|||
[Fact] |
|||
public void AccuracyOfIdentity1dTransformSize16Test() |
|||
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size16x16, 1); |
|||
|
|||
[Fact] |
|||
public void AccuracyOfIdentity1dTransformSize32Test() |
|||
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size32x32, 4); |
|||
|
|||
[Fact] |
|||
public void AccuracyOfIdentity1dTransformSize64Test() |
|||
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size64x64, 4); |
|||
|
|||
[Fact] |
|||
public void AccuracyOfEchoTransformSize4Test() |
|||
=> AssertAccuracy1d(Av1TransformType.Identity, Av1TransformSize.Size4x4, 0, new EchoTestTransformer(), new EchoTestTransformer()); |
|||
|
|||
private static void AssertAccuracy1d( |
|||
Av1TransformType transformType, |
|||
Av1TransformSize transformSize, |
|||
int scaleLog2, |
|||
int allowedError = 1) |
|||
{ |
|||
Av1Transform2dFlipConfiguration config = new(transformType, transformSize); |
|||
IAv1Forward1dTransformer forward = GetForwardTransformer(config.TransformFunctionTypeColumn); |
|||
IAv1Forward1dTransformer inverse = GetInverseTransformer(config.TransformFunctionTypeColumn); |
|||
AssertAccuracy1d(transformType, transformSize, scaleLog2, forward, inverse, allowedError); |
|||
} |
|||
|
|||
private static void AssertAccuracy1d( |
|||
Av1TransformType transformType, |
|||
Av1TransformSize transformSize, |
|||
int scaleLog2, |
|||
IAv1Forward1dTransformer forwardTransformer, |
|||
IAv1Forward1dTransformer inverseTransformer, |
|||
int allowedError = 1) |
|||
{ |
|||
const int bitDepth = 10; |
|||
Random rnd = new(0); |
|||
const int testBlockCount = 100; // Originally set to: 5000
|
|||
Av1Transform2dFlipConfiguration config = new(transformType, transformSize); |
|||
config.GenerateStageRange(bitDepth); |
|||
int width = config.TransformSize.GetWidth(); |
|||
|
|||
int[] inputOfTest = new int[width]; |
|||
int[] outputOfTest = new int[width]; |
|||
int[] outputReference = new int[width]; |
|||
for (int ti = 0; ti < testBlockCount; ++ti) |
|||
{ |
|||
// prepare random test data
|
|||
for (int ni = 0; ni < width; ++ni) |
|||
{ |
|||
inputOfTest[ni] = (short)rnd.Next((1 << bitDepth) - 1); |
|||
outputReference[ni] = 0; |
|||
outputOfTest[ni] = 255; |
|||
} |
|||
|
|||
// calculate in forward transform functions
|
|||
forwardTransformer.Transform( |
|||
inputOfTest, |
|||
outputReference, |
|||
config.CosBitColumn, |
|||
config.StageRangeColumn); |
|||
|
|||
// calculate in inverse transform functions
|
|||
inverseTransformer.Transform( |
|||
outputReference, |
|||
outputOfTest, |
|||
config.CosBitColumn, |
|||
config.StageRangeColumn); |
|||
|
|||
// Assert
|
|||
Assert.True(CompareWithError(inputOfTest, outputOfTest.Select(x => x >> scaleLog2).ToArray(), allowedError), $"Error: {GetMaximumError(inputOfTest, outputOfTest)}"); |
|||
} |
|||
} |
|||
|
|||
private static IAv1Forward1dTransformer GetForwardTransformer(Av1TransformFunctionType func) => |
|||
func switch |
|||
{ |
|||
Av1TransformFunctionType.Dct4 => new Av1Dct4Forward1dTransformer(), |
|||
Av1TransformFunctionType.Dct8 => new Av1Dct8Forward1dTransformer(), |
|||
Av1TransformFunctionType.Dct16 => new Av1Dct16Forward1dTransformer(), |
|||
Av1TransformFunctionType.Dct32 => new Av1Dct32Forward1dTransformer(), |
|||
Av1TransformFunctionType.Dct64 => new Av1Dct64Forward1dTransformer(), |
|||
Av1TransformFunctionType.Adst4 => new Av1Adst4Forward1dTransformer(), |
|||
Av1TransformFunctionType.Adst8 => new Av1Adst8Forward1dTransformer(), |
|||
Av1TransformFunctionType.Adst16 => new Av1Adst16Forward1dTransformer(), |
|||
Av1TransformFunctionType.Adst32 => new Av1Adst32Forward1dTransformer(), |
|||
Av1TransformFunctionType.Identity4 => new Av1Identity4Forward1dTransformer(), |
|||
Av1TransformFunctionType.Identity8 => new Av1Identity8Forward1dTransformer(), |
|||
Av1TransformFunctionType.Identity16 => new Av1Identity16Forward1dTransformer(), |
|||
Av1TransformFunctionType.Identity32 => new Av1Identity32Forward1dTransformer(), |
|||
Av1TransformFunctionType.Identity64 => new Av1Identity64Forward1dTransformer(), |
|||
Av1TransformFunctionType.Invalid => null, |
|||
_ => null, |
|||
}; |
|||
|
|||
private static IAv1Forward1dTransformer GetInverseTransformer(Av1TransformFunctionType func) => |
|||
func switch |
|||
{ |
|||
Av1TransformFunctionType.Dct4 => new Av1Dct4Inverse1dTransformer(), |
|||
Av1TransformFunctionType.Dct8 => null, // new Av1Dct8Inverse1dTransformer(),
|
|||
Av1TransformFunctionType.Dct16 => null, // new Av1Dct16Inverse1dTransformer(),
|
|||
Av1TransformFunctionType.Dct32 => null, // new Av1Dct32Inverse1dTransformer(),
|
|||
Av1TransformFunctionType.Dct64 => null, // new Av1Dct64Inverse1dTransformer(),
|
|||
Av1TransformFunctionType.Adst4 => new Av1Adst4Inverse1dTransformer(), |
|||
Av1TransformFunctionType.Adst8 => null, // new Av1Adst8Inverse1dTransformer(),
|
|||
Av1TransformFunctionType.Adst16 => null, // new Av1Adst16Inverse1dTransformer(),
|
|||
Av1TransformFunctionType.Adst32 => null, // new Av1Adst32Inverse1dTransformer(),
|
|||
Av1TransformFunctionType.Identity4 => new Av1Identity4Inverse1dTransformer(), |
|||
Av1TransformFunctionType.Identity8 => new Av1Identity8Inverse1dTransformer(), |
|||
Av1TransformFunctionType.Identity16 => new Av1Identity16Inverse1dTransformer(), |
|||
Av1TransformFunctionType.Identity32 => new Av1Identity32Inverse1dTransformer(), |
|||
Av1TransformFunctionType.Identity64 => new Av1Identity64Inverse1dTransformer(), |
|||
Av1TransformFunctionType.Invalid => null, |
|||
_ => null, |
|||
}; |
|||
|
|||
private static bool CompareWithError(Span<int> expected, Span<int> actual, int allowedError) |
|||
{ |
|||
// compare for the result is within accuracy
|
|||
int maximumErrorInTest = GetMaximumError(expected, actual); |
|||
return maximumErrorInTest <= allowedError; |
|||
} |
|||
|
|||
private static int GetMaximumError(Span<int> expected, Span<int> actual) |
|||
{ |
|||
int maximumErrorInTest = 0; |
|||
int count = Math.Min(expected.Length, 32); |
|||
for (int ni = 0; ni < count; ++ni) |
|||
{ |
|||
maximumErrorInTest = Math.Max(maximumErrorInTest, Math.Abs(actual[ni] - expected[ni])); |
|||
} |
|||
|
|||
return maximumErrorInTest; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue