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