mirror of https://github.com/SixLabors/ImageSharp
10 changed files with 1600 additions and 182 deletions
@ -0,0 +1,395 @@ |
|||||
|
// <copyright file="DCT.cs" company="James Jackson-South">
|
||||
|
// Copyright (c) James Jackson-South and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
// </copyright>
|
||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.Formats |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Contains forward & inverse DCT implementations
|
||||
|
/// </summary>
|
||||
|
internal static class DCT |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Apply floating point IDCT transformation into dest, using a temporary block 'temp' provided by the caller (optimization)
|
||||
|
/// </summary>
|
||||
|
/// <param name="src">Source</param>
|
||||
|
/// <param name="dest">Destination</param>
|
||||
|
/// <param name="temp">Temporary block provided by the caller</param>
|
||||
|
public static void TransformIDCT(ref Block8x8F src, ref Block8x8F dest, ref Block8x8F temp) |
||||
|
{ |
||||
|
src.TransposeInto(ref temp); |
||||
|
IDCT8x4_LeftPart(ref temp, ref dest); |
||||
|
IDCT8x4_RightPart(ref temp, ref dest); |
||||
|
|
||||
|
dest.TransposeInto(ref temp); |
||||
|
|
||||
|
IDCT8x4_LeftPart(ref temp, ref dest); |
||||
|
IDCT8x4_RightPart(ref temp, ref dest); |
||||
|
|
||||
|
dest.MultiplyAllInplace(C_0_125); |
||||
|
} |
||||
|
|
||||
|
#pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore
|
||||
|
private static readonly Vector4 C_1_175876 = new Vector4(1.175876f); |
||||
|
private static readonly Vector4 C_1_961571 = new Vector4(-1.961571f); |
||||
|
private static readonly Vector4 C_0_390181 = new Vector4(-0.390181f); |
||||
|
private static readonly Vector4 C_0_899976 = new Vector4(-0.899976f); |
||||
|
private static readonly Vector4 C_2_562915 = new Vector4(-2.562915f); |
||||
|
private static readonly Vector4 C_0_298631 = new Vector4(0.298631f); |
||||
|
private static readonly Vector4 C_2_053120 = new Vector4(2.053120f); |
||||
|
private static readonly Vector4 C_3_072711 = new Vector4(3.072711f); |
||||
|
private static readonly Vector4 C_1_501321 = new Vector4(1.501321f); |
||||
|
private static readonly Vector4 C_0_541196 = new Vector4(0.541196f); |
||||
|
private static readonly Vector4 C_1_847759 = new Vector4(-1.847759f); |
||||
|
private static readonly Vector4 C_0_765367 = new Vector4(0.765367f); |
||||
|
|
||||
|
private static readonly Vector4 C_0_125 = new Vector4(0.1250f); |
||||
|
#pragma warning restore SA1310 // FieldNamesMustNotContainUnderscore
|
||||
|
private static readonly Vector4 InvSqrt2 = new Vector4(0.707107f); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Do IDCT internal operations on the left part of the block. Original source:
|
||||
|
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
|
||||
|
/// </summary>
|
||||
|
/// <param name="d">Destination block</param>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
internal static void IDCT8x4_LeftPart(ref Block8x8F s, ref Block8x8F d) |
||||
|
{ |
||||
|
Vector4 my1 = s.V1L; |
||||
|
Vector4 my7 = s.V7L; |
||||
|
Vector4 mz0 = my1 + my7; |
||||
|
|
||||
|
Vector4 my3 = s.V3L; |
||||
|
Vector4 mz2 = my3 + my7; |
||||
|
Vector4 my5 = s.V5L; |
||||
|
Vector4 mz1 = my3 + my5; |
||||
|
Vector4 mz3 = my1 + my5; |
||||
|
|
||||
|
Vector4 mz4 = ((mz0 + mz1) * C_1_175876); |
||||
|
|
||||
|
mz2 = (mz2 * C_1_961571) + mz4; |
||||
|
mz3 = (mz3 * C_0_390181) + mz4; |
||||
|
mz0 = mz0 * C_0_899976; |
||||
|
mz1 = mz1 * C_2_562915; |
||||
|
|
||||
|
Vector4 mb3 = (my7 * C_0_298631) + mz0 + mz2; |
||||
|
Vector4 mb2 = (my5 * C_2_053120) + mz1 + mz3; |
||||
|
Vector4 mb1 = (my3 * C_3_072711) + mz1 + mz2; |
||||
|
Vector4 mb0 = (my1 * C_1_501321) + mz0 + mz3; |
||||
|
|
||||
|
Vector4 my2 = s.V2L; |
||||
|
Vector4 my6 = s.V6L; |
||||
|
mz4 = (my2 + my6) * C_0_541196; |
||||
|
Vector4 my0 = s.V0L; |
||||
|
Vector4 my4 = s.V4L; |
||||
|
mz0 = my0 + my4; |
||||
|
mz1 = my0 - my4; |
||||
|
|
||||
|
mz2 = mz4 + (my6 * C_1_847759); |
||||
|
mz3 = mz4 + (my2 * C_0_765367); |
||||
|
|
||||
|
my0 = mz0 + mz3; |
||||
|
my3 = mz0 - mz3; |
||||
|
my1 = mz1 + mz2; |
||||
|
my2 = mz1 - mz2; |
||||
|
|
||||
|
d.V0L = my0 + mb0; |
||||
|
d.V7L = my0 - mb0; |
||||
|
d.V1L = my1 + mb1; |
||||
|
d.V6L = my1 - mb1; |
||||
|
d.V2L = my2 + mb2; |
||||
|
d.V5L = my2 - mb2; |
||||
|
d.V3L = my3 + mb3; |
||||
|
d.V4L = my3 - mb3; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Do IDCT internal operations on the right part of the block.
|
||||
|
/// Original source:
|
||||
|
/// https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L261
|
||||
|
/// </summary>
|
||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
||||
|
internal static void IDCT8x4_RightPart(ref Block8x8F s, ref Block8x8F d) |
||||
|
{ |
||||
|
Vector4 my1 = s.V1R; |
||||
|
Vector4 my7 = s.V7R; |
||||
|
Vector4 mz0 = my1 + my7; |
||||
|
|
||||
|
Vector4 my3 = s.V3R; |
||||
|
Vector4 mz2 = my3 + my7; |
||||
|
Vector4 my5 = s.V5R; |
||||
|
Vector4 mz1 = my3 + my5; |
||||
|
Vector4 mz3 = my1 + my5; |
||||
|
|
||||
|
Vector4 mz4 = (mz0 + mz1) * C_1_175876; |
||||
|
|
||||
|
mz2 = (mz2 * C_1_961571) + mz4; |
||||
|
mz3 = (mz3 * C_0_390181) + mz4; |
||||
|
mz0 = mz0 * C_0_899976; |
||||
|
mz1 = mz1 * C_2_562915; |
||||
|
|
||||
|
Vector4 mb3 = (my7 * C_0_298631) + mz0 + mz2; |
||||
|
Vector4 mb2 = (my5 * C_2_053120) + mz1 + mz3; |
||||
|
Vector4 mb1 = (my3 * C_3_072711) + mz1 + mz2; |
||||
|
Vector4 mb0 = (my1 * C_1_501321) + mz0 + mz3; |
||||
|
|
||||
|
Vector4 my2 = s.V2R; |
||||
|
Vector4 my6 = s.V6R; |
||||
|
mz4 = (my2 + my6) * C_0_541196; |
||||
|
Vector4 my0 = s.V0R; |
||||
|
Vector4 my4 = s.V4R; |
||||
|
mz0 = my0 + my4; |
||||
|
mz1 = my0 - my4; |
||||
|
|
||||
|
mz2 = mz4 + (my6 * C_1_847759); |
||||
|
mz3 = mz4 + (my2 * C_0_765367); |
||||
|
|
||||
|
my0 = mz0 + mz3; |
||||
|
my3 = mz0 - mz3; |
||||
|
my1 = mz1 + mz2; |
||||
|
my2 = mz1 - mz2; |
||||
|
|
||||
|
d.V0R = my0 + mb0; |
||||
|
d.V7R = my0 - mb0; |
||||
|
d.V1R = my1 + mb1; |
||||
|
d.V6R = my1 - mb1; |
||||
|
d.V2R = my2 + mb2; |
||||
|
d.V5R = my2 - mb2; |
||||
|
d.V3R = my3 + mb3; |
||||
|
d.V4R = my3 - mb3; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Original:
|
||||
|
/// <see>
|
||||
|
/// <cref>https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L15</cref>
|
||||
|
/// </see>
|
||||
|
/// </summary>
|
||||
|
/// <param name="s">Source</param>
|
||||
|
/// <param name="d">Destination</param>
|
||||
|
public static void FDCT8x4_LeftPart(ref Block8x8F s, ref Block8x8F d) |
||||
|
{ |
||||
|
Vector4 c0 = s.V0L; |
||||
|
Vector4 c1 = s.V7L; |
||||
|
Vector4 t0 = (c0 + c1); |
||||
|
Vector4 t7 = (c0 - c1); |
||||
|
|
||||
|
c1 = s.V6L; |
||||
|
c0 = s.V1L; |
||||
|
Vector4 t1 = (c0 + c1); |
||||
|
Vector4 t6 = (c0 - c1); |
||||
|
|
||||
|
c1 = s.V5L; |
||||
|
c0 = s.V2L; |
||||
|
Vector4 t2 = (c0 + c1); |
||||
|
Vector4 t5 = (c0 - c1); |
||||
|
|
||||
|
c0 = s.V3L; |
||||
|
c1 = s.V4L; |
||||
|
Vector4 t3 = (c0 + c1); |
||||
|
Vector4 t4 = (c0 - c1); |
||||
|
|
||||
|
/* |
||||
|
c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; |
||||
|
c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; |
||||
|
c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; |
||||
|
c1 = x[3]; c2 = x[4]; t3 = c1 + c2; t4 = c1 - c2; |
||||
|
*/ |
||||
|
|
||||
|
c0 = (t0 + t3); |
||||
|
Vector4 c3 = (t0 - t3); |
||||
|
c1 = (t1 + t2); |
||||
|
Vector4 c2 = (t1 - t2); |
||||
|
|
||||
|
/* |
||||
|
c0 = t0 + t3; c3 = t0 - t3; |
||||
|
c1 = t1 + t2; c2 = t1 - t2; |
||||
|
*/ |
||||
|
|
||||
|
d.V0L = c0 + c1; |
||||
|
d.V4L = c0 - c1; |
||||
|
|
||||
|
/*y[0] = c0 + c1; |
||||
|
y[4] = c0 - c1;*/ |
||||
|
|
||||
|
Vector4 w0 = new Vector4(0.541196f); |
||||
|
Vector4 w1 = new Vector4(1.306563f); |
||||
|
|
||||
|
|
||||
|
d.V2L = (w0 * c2) + (w1 * c3); |
||||
|
|
||||
|
d.V6L = (w0 * c3) - (w1 * c2); |
||||
|
/* |
||||
|
y[2] = c2 * r[6] + c3 * r[2]; |
||||
|
y[6] = c3 * r[6] - c2 * r[2]; |
||||
|
*/ |
||||
|
|
||||
|
w0 = new Vector4(1.175876f); |
||||
|
w1 = new Vector4(0.785695f); |
||||
|
c3 = ((w0 * t4) + (w1 * t7)); |
||||
|
c0 = ((w0 * t7) - (w1 * t4)); |
||||
|
/* |
||||
|
c3 = t4 * r[3] + t7 * r[5]; |
||||
|
c0 = t7 * r[3] - t4 * r[5]; |
||||
|
*/ |
||||
|
|
||||
|
w0 = new Vector4(1.387040f); |
||||
|
w1 = new Vector4(0.275899f); |
||||
|
c2 = ((w0 * t5) + (w1 * t6)); |
||||
|
c1 = ((w0 * t6) - (w1 * t5)); |
||||
|
/* |
||||
|
c2 = t5 * r[1] + t6 * r[7]; |
||||
|
c1 = t6 * r[1] - t5 * r[7]; |
||||
|
*/ |
||||
|
|
||||
|
d.V3L = (c0 - c2); |
||||
|
|
||||
|
d.V5L = (c3 - c1); |
||||
|
//y[5] = c3 - c1; y[3] = c0 - c2;
|
||||
|
|
||||
|
Vector4 invsqrt2 = new Vector4(0.707107f); |
||||
|
c0 = ((c0 + c2) * invsqrt2); |
||||
|
c3 = ((c3 + c1) * invsqrt2); |
||||
|
//c0 = (c0 + c2) * invsqrt2;
|
||||
|
//c3 = (c3 + c1) * invsqrt2;
|
||||
|
|
||||
|
d.V1L = (c0 + c3); |
||||
|
|
||||
|
d.V7L = (c0 - c3); |
||||
|
//y[1] = c0 + c3; y[7] = c0 - c3;
|
||||
|
|
||||
|
/*for(i = 0;i < 8;i++) |
||||
|
{ |
||||
|
y[i] *= invsqrt2h; |
||||
|
}*/ |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Original:
|
||||
|
/// <see>
|
||||
|
/// <cref>https://github.com/norishigefukushima/dct_simd/blob/master/dct/dct8x8_simd.cpp#L15</cref>
|
||||
|
/// </see>
|
||||
|
/// </summary>
|
||||
|
/// <param name="s">Source</param>
|
||||
|
/// <param name="d">Destination</param>
|
||||
|
public static void FDCT8x4_RightPart(ref Block8x8F s, ref Block8x8F d) |
||||
|
{ |
||||
|
Vector4 c0 = s.V0R; |
||||
|
Vector4 c1 = s.V7R; |
||||
|
Vector4 t0 = (c0 + c1); |
||||
|
Vector4 t7 = (c0 - c1); |
||||
|
|
||||
|
c1 = s.V6R; |
||||
|
c0 = s.V1R; |
||||
|
Vector4 t1 = (c0 + c1); |
||||
|
Vector4 t6 = (c0 - c1); |
||||
|
|
||||
|
c1 = s.V5R; |
||||
|
c0 = s.V2R; |
||||
|
Vector4 t2 = (c0 + c1); |
||||
|
Vector4 t5 = (c0 - c1); |
||||
|
|
||||
|
c0 = s.V3R; |
||||
|
c1 = s.V4R; |
||||
|
Vector4 t3 = (c0 + c1); |
||||
|
Vector4 t4 = (c0 - c1); |
||||
|
|
||||
|
/* |
||||
|
c1 = x[0]; c2 = x[7]; t0 = c1 + c2; t7 = c1 - c2; |
||||
|
c1 = x[1]; c2 = x[6]; t1 = c1 + c2; t6 = c1 - c2; |
||||
|
c1 = x[2]; c2 = x[5]; t2 = c1 + c2; t5 = c1 - c2; |
||||
|
c1 = x[3]; c2 = x[4]; t3 = c1 + c2; t4 = c1 - c2; |
||||
|
*/ |
||||
|
|
||||
|
c0 = (t0 + t3); |
||||
|
Vector4 c3 = (t0 - t3); |
||||
|
c1 = (t1 + t2); |
||||
|
Vector4 c2 = (t1 - t2); |
||||
|
|
||||
|
/* |
||||
|
c0 = t0 + t3; c3 = t0 - t3; |
||||
|
c1 = t1 + t2; c2 = t1 - t2; |
||||
|
*/ |
||||
|
|
||||
|
d.V0R = c0 + c1; |
||||
|
d.V4R = c0 - c1; |
||||
|
|
||||
|
/*y[0] = c0 + c1; |
||||
|
y[4] = c0 - c1;*/ |
||||
|
|
||||
|
Vector4 w0 = new Vector4(0.541196f); |
||||
|
Vector4 w1 = new Vector4(1.306563f); |
||||
|
|
||||
|
|
||||
|
d.V2R = (w0 * c2) + (w1 * c3); |
||||
|
|
||||
|
d.V6R = (w0 * c3) - (w1 * c2); |
||||
|
/* |
||||
|
y[2] = c2 * r[6] + c3 * r[2]; |
||||
|
y[6] = c3 * r[6] - c2 * r[2]; |
||||
|
*/ |
||||
|
|
||||
|
w0 = new Vector4(1.175876f); |
||||
|
w1 = new Vector4(0.785695f); |
||||
|
c3 = ((w0 * t4) + (w1 * t7)); |
||||
|
c0 = ((w0 * t7) - (w1 * t4)); |
||||
|
/* |
||||
|
c3 = t4 * r[3] + t7 * r[5]; |
||||
|
c0 = t7 * r[3] - t4 * r[5]; |
||||
|
*/ |
||||
|
|
||||
|
w0 = new Vector4(1.387040f); |
||||
|
w1 = new Vector4(0.275899f); |
||||
|
c2 = ((w0 * t5) + (w1 * t6)); |
||||
|
c1 = ((w0 * t6) - (w1 * t5)); |
||||
|
/* |
||||
|
c2 = t5 * r[1] + t6 * r[7]; |
||||
|
c1 = t6 * r[1] - t5 * r[7]; |
||||
|
*/ |
||||
|
|
||||
|
d.V3R = (c0 - c2); |
||||
|
|
||||
|
d.V5R = (c3 - c1); |
||||
|
//y[5] = c3 - c1; y[3] = c0 - c2;
|
||||
|
|
||||
|
c0 = ((c0 + c2) * InvSqrt2); |
||||
|
c3 = ((c3 + c1) * InvSqrt2); |
||||
|
//c0 = (c0 + c2) * invsqrt2;
|
||||
|
//c3 = (c3 + c1) * invsqrt2;
|
||||
|
|
||||
|
d.V1R = (c0 + c3); |
||||
|
|
||||
|
d.V7R = (c0 - c3); |
||||
|
//y[1] = c0 + c3; y[7] = c0 - c3;
|
||||
|
|
||||
|
/*for(i = 0;i < 8;i++) |
||||
|
{ |
||||
|
y[i] *= invsqrt2h; |
||||
|
}*/ |
||||
|
} |
||||
|
|
||||
|
public static void TransformFDCT(ref Block8x8F s, ref Block8x8F d, ref Block8x8F temp, bool offsetSourceByNeg128 = true) |
||||
|
{ |
||||
|
s.TransposeInto(ref temp); |
||||
|
if (offsetSourceByNeg128) |
||||
|
{ |
||||
|
temp.AddToAllInplace(new Vector4(-128)); |
||||
|
} |
||||
|
|
||||
|
FDCT8x4_LeftPart(ref temp, ref d); |
||||
|
FDCT8x4_RightPart(ref temp, ref d); |
||||
|
|
||||
|
d.TransposeInto(ref temp); |
||||
|
|
||||
|
FDCT8x4_LeftPart(ref temp, ref d); |
||||
|
FDCT8x4_RightPart(ref temp, ref d); |
||||
|
|
||||
|
d.MultiplyAllInplace(C_0_125); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,144 @@ |
|||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.Tests.Formats.Jpg |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
using ImageSharp.Formats; |
||||
|
using Xunit; |
||||
|
using Xunit.Abstractions; |
||||
|
|
||||
|
public class ReferenceImplementationsTests : UtilityTestClassBase |
||||
|
{ |
||||
|
public ReferenceImplementationsTests(ITestOutputHelper output) |
||||
|
: base(output) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(2)] |
||||
|
public void Idct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) |
||||
|
{ |
||||
|
MutableSpan<int> intData = Create8x8RandomIntData(-200, 200, seed); |
||||
|
MutableSpan<float> floatSrc = intData.ConvertToFloat32MutableSpan(); |
||||
|
|
||||
|
ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(intData); |
||||
|
|
||||
|
MutableSpan<float> dest = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> temp = new MutableSpan<float>(64); |
||||
|
|
||||
|
ReferenceImplementations.iDCT2D_llm(floatSrc, dest, temp); |
||||
|
|
||||
|
for (int i = 0; i < 64; i++) |
||||
|
{ |
||||
|
float expected = intData[i]; |
||||
|
float actual = dest[i]; |
||||
|
|
||||
|
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42, 0)] |
||||
|
[InlineData(1, 0)] |
||||
|
[InlineData(2, 0)] |
||||
|
public void IntegerDCT_ForwardThenInverse(int seed, int startAt) |
||||
|
{ |
||||
|
MutableSpan<int> original = Create8x8RandomIntData(-200, 200, seed); |
||||
|
|
||||
|
var block = original.AddScalarToAllValues(128); |
||||
|
|
||||
|
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(block); |
||||
|
|
||||
|
for (int i = 0; i < 64; i++) |
||||
|
{ |
||||
|
block[i] /= 8; |
||||
|
} |
||||
|
|
||||
|
ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(block); |
||||
|
|
||||
|
for (int i = startAt; i < 64; i++) |
||||
|
{ |
||||
|
float expected = original[i]; |
||||
|
float actual = (float)block[i]; |
||||
|
|
||||
|
Assert.Equal(expected, actual, new ApproximateFloatComparer(3f)); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42, 0)] |
||||
|
[InlineData(1, 0)] |
||||
|
[InlineData(2, 0)] |
||||
|
public void FloatingPointDCT_ReferenceImplementation_ForwardThenInverse(int seed, int startAt) |
||||
|
{ |
||||
|
var data = Create8x8RandomIntData(-200, 200, seed); |
||||
|
MutableSpan<float> src = new MutableSpan<int>(data).ConvertToFloat32MutableSpan(); |
||||
|
MutableSpan<float> dest = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> temp = new MutableSpan<float>(64); |
||||
|
|
||||
|
ReferenceImplementations.fDCT2D_llm(src, dest, temp, true); |
||||
|
ReferenceImplementations.iDCT2D_llm(dest, src, temp); |
||||
|
|
||||
|
for (int i = startAt; i < 64; i++) |
||||
|
{ |
||||
|
float expected = data[i]; |
||||
|
float actual = (float)src[i]; |
||||
|
|
||||
|
Assert.Equal(expected, actual, new ApproximateFloatComparer(2f)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void HowMuchIsTheFish() |
||||
|
{ |
||||
|
Output.WriteLine(Vector<int>.Count.ToString()); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(2)] |
||||
|
public void Fdct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) |
||||
|
{ |
||||
|
MutableSpan<int> intData = Create8x8RandomIntData(-200, 200, seed); |
||||
|
MutableSpan<float> floatSrc = intData.ConvertToFloat32MutableSpan(); |
||||
|
|
||||
|
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(intData); |
||||
|
|
||||
|
MutableSpan<float> dest = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> temp = new MutableSpan<float>(64); |
||||
|
|
||||
|
ReferenceImplementations.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true); |
||||
|
|
||||
|
for (int i = 0; i < 64; i++) |
||||
|
{ |
||||
|
float expected = intData[i]; |
||||
|
float actual = dest[i]; |
||||
|
|
||||
|
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(2)] |
||||
|
public void Fdct_SimdReferenceImplementation_IsEquivalentToFloatingPointReferenceImplementation(int seed) |
||||
|
{ |
||||
|
Block classic = new Block() { Data = Create8x8RandomIntData(-200, 200, seed) }; |
||||
|
MutableSpan<float> src = new MutableSpan<int>(classic.Data).ConvertToFloat32MutableSpan(); |
||||
|
|
||||
|
MutableSpan<float> dest1 = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> dest2 = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> temp = new MutableSpan<float>(64); |
||||
|
|
||||
|
ReferenceImplementations.fDCT2D_llm(src, dest1, temp, downscaleBy8: true, offsetSourceByNeg128: false); |
||||
|
ReferenceImplementations.fDCT8x8_llm_sse(src, dest2, temp); |
||||
|
|
||||
|
Assert.Equal(dest1.Data, dest2.Data, new ApproximateFloatComparer(1f)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,144 @@ |
|||||
|
// ReSharper disable InconsistentNaming
|
||||
|
namespace ImageSharp.Tests.Formats.Jpg |
||||
|
{ |
||||
|
using System.Numerics; |
||||
|
using ImageSharp.Formats; |
||||
|
using Xunit; |
||||
|
using Xunit.Abstractions; |
||||
|
|
||||
|
public class ReferenceImplementationsTests : UtilityTestClassBase |
||||
|
{ |
||||
|
public ReferenceImplementationsTests(ITestOutputHelper output) |
||||
|
: base(output) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(2)] |
||||
|
public void Idct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) |
||||
|
{ |
||||
|
MutableSpan<int> intData = Create8x8RandomIntData(-200, 200, seed); |
||||
|
MutableSpan<float> floatSrc = intData.ConvertToFloat32MutableSpan(); |
||||
|
|
||||
|
ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(intData); |
||||
|
|
||||
|
MutableSpan<float> dest = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> temp = new MutableSpan<float>(64); |
||||
|
|
||||
|
ReferenceImplementations.iDCT2D_llm(floatSrc, dest, temp); |
||||
|
|
||||
|
for (int i = 0; i < 64; i++) |
||||
|
{ |
||||
|
float expected = intData[i]; |
||||
|
float actual = dest[i]; |
||||
|
|
||||
|
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42, 0)] |
||||
|
[InlineData(1, 0)] |
||||
|
[InlineData(2, 0)] |
||||
|
public void IntegerDCT_ForwardThenInverse(int seed, int startAt) |
||||
|
{ |
||||
|
MutableSpan<int> original = Create8x8RandomIntData(-200, 200, seed); |
||||
|
|
||||
|
var block = original.AddScalarToAllValues(128); |
||||
|
|
||||
|
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(block); |
||||
|
|
||||
|
for (int i = 0; i < 64; i++) |
||||
|
{ |
||||
|
block[i] /= 8; |
||||
|
} |
||||
|
|
||||
|
ReferenceImplementations.IntegerReferenceDCT.TransformIDCTInplace(block); |
||||
|
|
||||
|
for (int i = startAt; i < 64; i++) |
||||
|
{ |
||||
|
float expected = original[i]; |
||||
|
float actual = (float)block[i]; |
||||
|
|
||||
|
Assert.Equal(expected, actual, new ApproximateFloatComparer(3f)); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42, 0)] |
||||
|
[InlineData(1, 0)] |
||||
|
[InlineData(2, 0)] |
||||
|
public void FloatingPointDCT_ReferenceImplementation_ForwardThenInverse(int seed, int startAt) |
||||
|
{ |
||||
|
var data = Create8x8RandomIntData(-200, 200, seed); |
||||
|
MutableSpan<float> src = new MutableSpan<int>(data).ConvertToFloat32MutableSpan(); |
||||
|
MutableSpan<float> dest = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> temp = new MutableSpan<float>(64); |
||||
|
|
||||
|
ReferenceImplementations.fDCT2D_llm(src, dest, temp, true); |
||||
|
ReferenceImplementations.iDCT2D_llm(dest, src, temp); |
||||
|
|
||||
|
for (int i = startAt; i < 64; i++) |
||||
|
{ |
||||
|
float expected = data[i]; |
||||
|
float actual = (float)src[i]; |
||||
|
|
||||
|
Assert.Equal(expected, actual, new ApproximateFloatComparer(2f)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void HowMuchIsTheFish() |
||||
|
{ |
||||
|
Output.WriteLine(Vector<int>.Count.ToString()); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(2)] |
||||
|
public void Fdct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed) |
||||
|
{ |
||||
|
MutableSpan<int> intData = Create8x8RandomIntData(-200, 200, seed); |
||||
|
MutableSpan<float> floatSrc = intData.ConvertToFloat32MutableSpan(); |
||||
|
|
||||
|
ReferenceImplementations.IntegerReferenceDCT.TransformFDCTInplace(intData); |
||||
|
|
||||
|
MutableSpan<float> dest = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> temp = new MutableSpan<float>(64); |
||||
|
|
||||
|
ReferenceImplementations.fDCT2D_llm(floatSrc, dest, temp, offsetSourceByNeg128: true); |
||||
|
|
||||
|
for (int i = 0; i < 64; i++) |
||||
|
{ |
||||
|
float expected = intData[i]; |
||||
|
float actual = dest[i]; |
||||
|
|
||||
|
Assert.Equal(expected, actual, new ApproximateFloatComparer(1f)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(42)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(2)] |
||||
|
public void Fdct_SimdReferenceImplementation_IsEquivalentToFloatingPointReferenceImplementation(int seed) |
||||
|
{ |
||||
|
Block classic = new Block() { Data = Create8x8RandomIntData(-200, 200, seed) }; |
||||
|
MutableSpan<float> src = new MutableSpan<int>(classic.Data).ConvertToFloat32MutableSpan(); |
||||
|
|
||||
|
MutableSpan<float> dest1 = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> dest2 = new MutableSpan<float>(64); |
||||
|
MutableSpan<float> temp = new MutableSpan<float>(64); |
||||
|
|
||||
|
ReferenceImplementations.fDCT2D_llm(src, dest1, temp, downscaleBy8: true, offsetSourceByNeg128: false); |
||||
|
ReferenceImplementations.fDCT8x8_llm_sse(src, dest2, temp); |
||||
|
|
||||
|
Assert.Equal(dest1.Data, dest2.Data, new ApproximateFloatComparer(1f)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue