mirror of https://github.com/SixLabors/ImageSharp
10 changed files with 2126 additions and 136 deletions
@ -0,0 +1,55 @@ |
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
|
|||
namespace ImageSharp.Formats |
|||
{ |
|||
internal partial struct Block8x8F |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void TransposeInto(ref Block8x8F d) |
|||
{ |
|||
d.V0L.X = V0L.X; d.V1L.X = V0L.Y; d.V2L.X = V0L.Z; d.V3L.X = V0L.W; d.V4L.X = V0R.X; d.V5L.X = V0R.Y; d.V6L.X = V0R.Z; d.V7L.X = V0R.W; |
|||
d.V0L.Y = V1L.X; d.V1L.Y = V1L.Y; d.V2L.Y = V1L.Z; d.V3L.Y = V1L.W; d.V4L.Y = V1R.X; d.V5L.Y = V1R.Y; d.V6L.Y = V1R.Z; d.V7L.Y = V1R.W; |
|||
d.V0L.Z = V2L.X; d.V1L.Z = V2L.Y; d.V2L.Z = V2L.Z; d.V3L.Z = V2L.W; d.V4L.Z = V2R.X; d.V5L.Z = V2R.Y; d.V6L.Z = V2R.Z; d.V7L.Z = V2R.W; |
|||
d.V0L.W = V3L.X; d.V1L.W = V3L.Y; d.V2L.W = V3L.Z; d.V3L.W = V3L.W; d.V4L.W = V3R.X; d.V5L.W = V3R.Y; d.V6L.W = V3R.Z; d.V7L.W = V3R.W; |
|||
d.V0R.X = V4L.X; d.V1R.X = V4L.Y; d.V2R.X = V4L.Z; d.V3R.X = V4L.W; d.V4R.X = V4R.X; d.V5R.X = V4R.Y; d.V6R.X = V4R.Z; d.V7R.X = V4R.W; |
|||
d.V0R.Y = V5L.X; d.V1R.Y = V5L.Y; d.V2R.Y = V5L.Z; d.V3R.Y = V5L.W; d.V4R.Y = V5R.X; d.V5R.Y = V5R.Y; d.V6R.Y = V5R.Z; d.V7R.Y = V5R.W; |
|||
d.V0R.Z = V6L.X; d.V1R.Z = V6L.Y; d.V2R.Z = V6L.Z; d.V3R.Z = V6L.W; d.V4R.Z = V6R.X; d.V5R.Z = V6R.Y; d.V6R.Z = V6R.Z; d.V7R.Z = V6R.W; |
|||
d.V0R.W = V7L.X; d.V1R.W = V7L.Y; d.V2R.W = V7L.Z; d.V3R.W = V7L.W; d.V4R.W = V7R.X; d.V5R.W = V7R.Y; d.V6R.W = V7R.Z; d.V7R.W = V7R.W; |
|||
} |
|||
|
|||
|
|||
public void CropInto(float min, float max, ref Block8x8F d) |
|||
{ |
|||
Vector4 minVec = new Vector4(min); |
|||
Vector4 maxVec = new Vector4(max); |
|||
|
|||
d.V0L = Vector4.Max(Vector4.Min(V0L, maxVec), minVec);d.V0R = Vector4.Max(Vector4.Min(V0R, maxVec), minVec); |
|||
d.V1L = Vector4.Max(Vector4.Min(V1L, maxVec), minVec);d.V1R = Vector4.Max(Vector4.Min(V1R, maxVec), minVec); |
|||
d.V2L = Vector4.Max(Vector4.Min(V2L, maxVec), minVec);d.V2R = Vector4.Max(Vector4.Min(V2R, maxVec), minVec); |
|||
d.V3L = Vector4.Max(Vector4.Min(V3L, maxVec), minVec);d.V3R = Vector4.Max(Vector4.Min(V3R, maxVec), minVec); |
|||
d.V4L = Vector4.Max(Vector4.Min(V4L, maxVec), minVec);d.V4R = Vector4.Max(Vector4.Min(V4R, maxVec), minVec); |
|||
d.V5L = Vector4.Max(Vector4.Min(V5L, maxVec), minVec);d.V5R = Vector4.Max(Vector4.Min(V5R, maxVec), minVec); |
|||
d.V6L = Vector4.Max(Vector4.Min(V6L, maxVec), minVec);d.V6R = Vector4.Max(Vector4.Min(V6R, maxVec), minVec); |
|||
d.V7L = Vector4.Max(Vector4.Min(V7L, maxVec), minVec);d.V7R = Vector4.Max(Vector4.Min(V7R, maxVec), minVec); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal void ColorifyInto(ref Block8x8F d) |
|||
{ |
|||
d.V0L = Vector4.Max(Vector4.Min(V0L, CMax4), CMin4) + COff4;d.V0R = Vector4.Max(Vector4.Min(V0R, CMax4), CMin4) + COff4; |
|||
d.V1L = Vector4.Max(Vector4.Min(V1L, CMax4), CMin4) + COff4;d.V1R = Vector4.Max(Vector4.Min(V1R, CMax4), CMin4) + COff4; |
|||
d.V2L = Vector4.Max(Vector4.Min(V2L, CMax4), CMin4) + COff4;d.V2R = Vector4.Max(Vector4.Min(V2R, CMax4), CMin4) + COff4; |
|||
d.V3L = Vector4.Max(Vector4.Min(V3L, CMax4), CMin4) + COff4;d.V3R = Vector4.Max(Vector4.Min(V3R, CMax4), CMin4) + COff4; |
|||
d.V4L = Vector4.Max(Vector4.Min(V4L, CMax4), CMin4) + COff4;d.V4R = Vector4.Max(Vector4.Min(V4R, CMax4), CMin4) + COff4; |
|||
d.V5L = Vector4.Max(Vector4.Min(V5L, CMax4), CMin4) + COff4;d.V5R = Vector4.Max(Vector4.Min(V5R, CMax4), CMin4) + COff4; |
|||
d.V6L = Vector4.Max(Vector4.Min(V6L, CMax4), CMin4) + COff4;d.V6R = Vector4.Max(Vector4.Min(V6R, CMax4), CMin4) + COff4; |
|||
d.V7L = Vector4.Max(Vector4.Min(V7L, CMax4), CMin4) + COff4;d.V7R = Vector4.Max(Vector4.Min(V7R, CMax4), CMin4) + COff4; |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,93 @@ |
|||
<#@ template debug="false" hostspecific="false" language="C#" #> |
|||
<#@ assembly name="System.Core" #> |
|||
<#@ import namespace="System.Linq" #> |
|||
<#@ import namespace="System.Text" #> |
|||
<#@ import namespace="System.Collections.Generic" #> |
|||
<#@ output extension=".cs" #> |
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
<# |
|||
char[] coordz = new[] {'X', 'Y', 'Z', 'W'}; |
|||
#> |
|||
|
|||
namespace ImageSharp.Formats |
|||
{ |
|||
internal partial struct Block8x8F |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void TransposeInto(ref Block8x8F d) |
|||
{ |
|||
<# |
|||
PushIndent(" "); |
|||
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
char destCoord = coordz[i % 4]; |
|||
char destSide = (i / 4) % 2 == 0 ? 'L' : 'R'; |
|||
|
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
char srcCoord = coordz[j % 4]; |
|||
char srcSide = (j / 4) % 2 == 0 ? 'L' : 'R'; |
|||
|
|||
string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; "; |
|||
//bld.Append(expression); |
|||
Write(expression); |
|||
} |
|||
//bld.AppendLine(); |
|||
WriteLine(""); |
|||
} |
|||
PopIndent(); |
|||
//Write(bld.ToString()); |
|||
#> |
|||
} |
|||
|
|||
|
|||
public void CropInto(float min, float max, ref Block8x8F d) |
|||
{ |
|||
Vector4 minVec = new Vector4(min); |
|||
Vector4 maxVec = new Vector4(max); |
|||
|
|||
<# |
|||
|
|||
PushIndent(" "); |
|||
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
for (int j = 0; j < 2; j++) |
|||
{ |
|||
char side = j == 0 ? 'L' : 'R'; |
|||
Write($"d.V{i}{side} = Vector4.Max(Vector4.Min(V{i}{side}, maxVec), minVec);"); |
|||
} |
|||
WriteLine(""); |
|||
} |
|||
PopIndent(); |
|||
#> |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal void ColorifyInto(ref Block8x8F d) |
|||
{ |
|||
<# |
|||
|
|||
PushIndent(" "); |
|||
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
for (int j = 0; j < 2; j++) |
|||
{ |
|||
char side = j == 0 ? 'L' : 'R'; |
|||
Write($"d.V{i}{side} = Vector4.Max(Vector4.Min(V{i}{side}, CMax4), CMin4) + COff4;"); |
|||
} |
|||
WriteLine(""); |
|||
} |
|||
PopIndent(); |
|||
#> |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,617 @@ |
|||
using System; |
|||
using System.Buffers; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
|
|||
namespace ImageSharp.Formats |
|||
{ |
|||
/// <summary>
|
|||
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd
|
|||
/// </summary>
|
|||
internal partial struct Block8x8F |
|||
{ |
|||
public Vector4 V0L; |
|||
public Vector4 V0R; |
|||
|
|||
public Vector4 V1L; |
|||
public Vector4 V1R; |
|||
|
|||
public Vector4 V2L; |
|||
public Vector4 V2R; |
|||
|
|||
public Vector4 V3L; |
|||
public Vector4 V3R; |
|||
|
|||
public Vector4 V4L; |
|||
public Vector4 V4R; |
|||
|
|||
public Vector4 V5L; |
|||
public Vector4 V5R; |
|||
|
|||
public Vector4 V6L; |
|||
public Vector4 V6R; |
|||
|
|||
public Vector4 V7L; |
|||
public Vector4 V7R; |
|||
|
|||
|
|||
public const int VectorCount = 16; |
|||
public const int ScalarCount = VectorCount*4; |
|||
|
|||
private static readonly ArrayPool<float> ScalarArrayPool = ArrayPool<float>.Create(ScalarCount, 50); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public unsafe void LoadFrom(MutableSpan<float> source) |
|||
{ |
|||
fixed (Vector4* ptr = &V0L) |
|||
{ |
|||
Marshal.Copy(source.Data, source.Offset, (IntPtr) ptr, ScalarCount); |
|||
//float* fp = (float*)ptr;
|
|||
//for (int i = 0; i < ScalarCount; i++)
|
|||
//{
|
|||
// fp[i] = source[i];
|
|||
//}
|
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public unsafe void CopyTo(MutableSpan<float> dest) |
|||
{ |
|||
fixed (Vector4* ptr = &V0L) |
|||
{ |
|||
Marshal.Copy((IntPtr) ptr, dest.Data, dest.Offset, ScalarCount); |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public unsafe void CopyTo(float[] dest) |
|||
{ |
|||
fixed (Vector4* ptr = &V0L) |
|||
{ |
|||
Marshal.Copy((IntPtr) ptr, dest, 0, ScalarCount); |
|||
} |
|||
} |
|||
|
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static unsafe void LoadFrom(Block8x8F* blockPtr, MutableSpan<float> source) |
|||
{ |
|||
Marshal.Copy(source.Data, source.Offset, (IntPtr) blockPtr, ScalarCount); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static unsafe void CopyTo(Block8x8F* blockPtr, MutableSpan<float> dest) |
|||
{ |
|||
Marshal.Copy((IntPtr) blockPtr, dest.Data, dest.Offset, ScalarCount); |
|||
} |
|||
|
|||
|
|||
internal unsafe void LoadFrom(MutableSpan<int> source) |
|||
{ |
|||
fixed (Vector4* ptr = &V0L) |
|||
{ |
|||
float* fp = (float*) ptr; |
|||
for (int i = 0; i < ScalarCount; i++) |
|||
{ |
|||
fp[i] = source[i]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal unsafe void CopyTo(MutableSpan<int> dest) |
|||
{ |
|||
fixed (Vector4* ptr = &V0L) |
|||
{ |
|||
float* fp = (float*) ptr; |
|||
for (int i = 0; i < ScalarCount; i++) |
|||
{ |
|||
dest[i] = (int) fp[i]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public unsafe void TransposeInplace() |
|||
{ |
|||
fixed (Vector4* ptr = &V0L) |
|||
{ |
|||
float* data = (float*) ptr; |
|||
|
|||
for (int i = 1; i < 8; i++) |
|||
{ |
|||
int i8 = i*8; |
|||
for (int j = 0; j < i; j++) |
|||
{ |
|||
float tmp = data[i8 + j]; |
|||
data[i8 + j] = data[j*8 + i]; |
|||
data[j*8 + i] = tmp; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reference implementation we can benchmark against
|
|||
/// </summary>
|
|||
internal unsafe void TransposeInto_PinningImpl(ref Block8x8F destination) |
|||
{ |
|||
fixed (Vector4* sPtr = &V0L) |
|||
{ |
|||
float* src = (float*) sPtr; |
|||
|
|||
fixed (Vector4* dPtr = &destination.V0L) |
|||
{ |
|||
float* dest = (float*) dPtr; |
|||
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
int i8 = i*8; |
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
dest[j*8 + i] = src[i8 + j]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static unsafe void TransposeInto(Block8x8F* sourcePtr, Block8x8F* destPtr) |
|||
{ |
|||
float* src = (float*) sourcePtr; |
|||
float* dest = (float*) destPtr; |
|||
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
int i8 = i*8; |
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
dest[j*8 + i] = src[i8 + j]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void MultiplyAllInplace(Vector4 s) |
|||
{ |
|||
V0L *= s; |
|||
V0R *= s; |
|||
V1L *= s; |
|||
V1R *= s; |
|||
V2L *= s; |
|||
V2R *= s; |
|||
V3L *= s; |
|||
V3R *= s; |
|||
V4L *= s; |
|||
V4R *= s; |
|||
V5L *= s; |
|||
V5R *= s; |
|||
V6L *= s; |
|||
V6R *= s; |
|||
V7L *= s; |
|||
V7R *= s; |
|||
} |
|||
|
|||
// ReSharper disable once InconsistentNaming
|
|||
public void IDCTInto(ref Block8x8F dest, ref Block8x8F temp) |
|||
{ |
|||
TransposeInto(ref temp); |
|||
temp.iDCT2D8x4_LeftPart(ref dest); |
|||
temp.iDCT2D8x4_RightPart(ref dest); |
|||
|
|||
dest.TransposeInto(ref temp); |
|||
|
|||
temp.iDCT2D8x4_LeftPart(ref dest); |
|||
temp.iDCT2D8x4_RightPart(ref dest); |
|||
|
|||
dest.MultiplyAllInplace(_0_125); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void IDCTInplace() |
|||
{ |
|||
Block8x8F result = new Block8x8F(); |
|||
Block8x8F temp = new Block8x8F(); |
|||
IDCTInto(ref result, ref temp); |
|||
this = result; |
|||
} |
|||
|
|||
private static readonly Vector4 _1_175876 = new Vector4(1.175876f); |
|||
private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); |
|||
private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); |
|||
private static readonly Vector4 _0_899976 = new Vector4(-0.899976f); |
|||
private static readonly Vector4 _2_562915 = new Vector4(-2.562915f); |
|||
private static readonly Vector4 _0_298631 = new Vector4(0.298631f); |
|||
private static readonly Vector4 _2_053120 = new Vector4(2.053120f); |
|||
private static readonly Vector4 _3_072711 = new Vector4(3.072711f); |
|||
private static readonly Vector4 _1_501321 = new Vector4(1.501321f); |
|||
private static readonly Vector4 _0_541196 = new Vector4(0.541196f); |
|||
private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); |
|||
private static readonly Vector4 _0_765367 = new Vector4(0.765367f); |
|||
private static readonly Vector4 _0_125 = new Vector4(0.1250f); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal void iDCT2D8x4_LeftPart(ref Block8x8F d) |
|||
{ |
|||
/* |
|||
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; |
|||
for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } |
|||
*/ |
|||
/* |
|||
0: 1.414214 |
|||
1: 1.387040 |
|||
2: 1.306563 |
|||
3: |
|||
4: 1.000000 |
|||
5: 0.785695 |
|||
6: |
|||
7: 0.275899 |
|||
*/ |
|||
|
|||
Vector4 my1 = V1L; |
|||
Vector4 my7 = V7L; |
|||
Vector4 mz0 = my1 + my7; |
|||
|
|||
Vector4 my3 = V3L; |
|||
Vector4 mz2 = my3 + my7; |
|||
Vector4 my5 = V5L; |
|||
Vector4 mz1 = my3 + my5; |
|||
Vector4 mz3 = my1 + my5; |
|||
|
|||
Vector4 mz4 = ((mz0 + mz1)*_1_175876); |
|||
//z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5];
|
|||
//z4 = (z0 + z1) * r[3];
|
|||
|
|||
mz2 = mz2*_1_961571 + mz4; |
|||
mz3 = mz3*_0_390181 + mz4; |
|||
mz0 = mz0*_0_899976; |
|||
mz1 = mz1*_2_562915; |
|||
|
|||
/* |
|||
-0.899976 |
|||
-2.562915 |
|||
-1.961571 |
|||
-0.390181 |
|||
z0 = z0 * (-r[3] + r[7]); |
|||
z1 = z1 * (-r[3] - r[1]); |
|||
z2 = z2 * (-r[3] - r[5]) + z4; |
|||
z3 = z3 * (-r[3] + r[5]) + z4;*/ |
|||
|
|||
|
|||
Vector4 mb3 = my7*_0_298631 + mz0 + mz2; |
|||
Vector4 mb2 = my5*_2_053120 + mz1 + mz3; |
|||
Vector4 mb1 = my3*_3_072711 + mz1 + mz2; |
|||
Vector4 mb0 = my1*_1_501321 + mz0 + mz3; |
|||
|
|||
/* |
|||
0.298631 |
|||
2.053120 |
|||
3.072711 |
|||
1.501321 |
|||
b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; |
|||
b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; |
|||
b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; |
|||
b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; |
|||
*/ |
|||
|
|||
Vector4 my2 = V2L; |
|||
Vector4 my6 = V6L; |
|||
mz4 = (my2 + my6)*_0_541196; |
|||
Vector4 my0 = V0L; |
|||
Vector4 my4 = V4L; |
|||
mz0 = my0 + my4; |
|||
mz1 = my0 - my4; |
|||
|
|||
mz2 = mz4 + my6*_1_847759; |
|||
mz3 = mz4 + my2*_0_765367; |
|||
|
|||
my0 = mz0 + mz3; |
|||
my3 = mz0 - mz3; |
|||
my1 = mz1 + mz2; |
|||
my2 = mz1 - mz2; |
|||
/* |
|||
1.847759 |
|||
0.765367 |
|||
z4 = (y[2] + y[6]) * r[6]; |
|||
z0 = y[0] + y[4]; z1 = y[0] - y[4]; |
|||
z2 = z4 - y[6] * (r[2] + r[6]); |
|||
z3 = z4 + y[2] * (r[2] - r[6]); |
|||
a0 = z0 + z3; a3 = z0 - z3; |
|||
a1 = z1 + z2; a2 = z1 - z2; |
|||
*/ |
|||
|
|||
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; |
|||
/* |
|||
x[0] = a0 + b0; x[7] = a0 - b0; |
|||
x[1] = a1 + b1; x[6] = a1 - b1; |
|||
x[2] = a2 + b2; x[5] = a2 - b2; |
|||
x[3] = a3 + b3; x[4] = a3 - b3; |
|||
for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } |
|||
*/ |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal void iDCT2D8x4_RightPart(ref Block8x8F d) |
|||
{ |
|||
/* |
|||
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; |
|||
for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } |
|||
*/ |
|||
/* |
|||
0: 1.414214 |
|||
1: 1.387040 |
|||
2: 1.306563 |
|||
3: |
|||
4: 1.000000 |
|||
5: 0.785695 |
|||
6: |
|||
7: 0.275899 |
|||
*/ |
|||
|
|||
Vector4 my1 = V1R; |
|||
Vector4 my7 = V7R; |
|||
Vector4 mz0 = my1 + my7; |
|||
|
|||
Vector4 my3 = V3R; |
|||
Vector4 mz2 = my3 + my7; |
|||
Vector4 my5 = V5R; |
|||
Vector4 mz1 = my3 + my5; |
|||
Vector4 mz3 = my1 + my5; |
|||
|
|||
Vector4 mz4 = ((mz0 + mz1)*_1_175876); |
|||
//z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5];
|
|||
//z4 = (z0 + z1) * r[3];
|
|||
|
|||
mz2 = mz2*_1_961571 + mz4; |
|||
mz3 = mz3*_0_390181 + mz4; |
|||
mz0 = mz0*_0_899976; |
|||
mz1 = mz1*_2_562915; |
|||
|
|||
/* |
|||
-0.899976 |
|||
-2.562915 |
|||
-1.961571 |
|||
-0.390181 |
|||
z0 = z0 * (-r[3] + r[7]); |
|||
z1 = z1 * (-r[3] - r[1]); |
|||
z2 = z2 * (-r[3] - r[5]) + z4; |
|||
z3 = z3 * (-r[3] + r[5]) + z4;*/ |
|||
|
|||
|
|||
Vector4 mb3 = my7*_0_298631 + mz0 + mz2; |
|||
Vector4 mb2 = my5*_2_053120 + mz1 + mz3; |
|||
Vector4 mb1 = my3*_3_072711 + mz1 + mz2; |
|||
Vector4 mb0 = my1*_1_501321 + mz0 + mz3; |
|||
|
|||
/* |
|||
0.298631 |
|||
2.053120 |
|||
3.072711 |
|||
1.501321 |
|||
b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; |
|||
b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; |
|||
b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; |
|||
b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; |
|||
*/ |
|||
|
|||
Vector4 my2 = V2R; |
|||
Vector4 my6 = V6R; |
|||
mz4 = (my2 + my6)*_0_541196; |
|||
Vector4 my0 = V0R; |
|||
Vector4 my4 = V4R; |
|||
mz0 = my0 + my4; |
|||
mz1 = my0 - my4; |
|||
|
|||
mz2 = mz4 + my6*_1_847759; |
|||
mz3 = mz4 + my2*_0_765367; |
|||
|
|||
my0 = mz0 + mz3; |
|||
my3 = mz0 - mz3; |
|||
my1 = mz1 + mz2; |
|||
my2 = mz1 - mz2; |
|||
/* |
|||
1.847759 |
|||
0.765367 |
|||
z4 = (y[2] + y[6]) * r[6]; |
|||
z0 = y[0] + y[4]; z1 = y[0] - y[4]; |
|||
z2 = z4 - y[6] * (r[2] + r[6]); |
|||
z3 = z4 + y[2] * (r[2] - r[6]); |
|||
a0 = z0 + z3; a3 = z0 - z3; |
|||
a1 = z1 + z2; a2 = z1 - z2; |
|||
*/ |
|||
|
|||
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; |
|||
/* |
|||
x[0] = a0 + b0; x[7] = a0 - b0; |
|||
x[1] = a1 + b1; x[6] = a1 - b1; |
|||
x[2] = a2 + b2; x[5] = a2 - b2; |
|||
x[3] = a3 + b3; x[4] = a3 - b3; |
|||
for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } |
|||
*/ |
|||
} |
|||
|
|||
internal static void SuchIDCT(ref Block block) |
|||
{ |
|||
Block8x8F source = new Block8x8F(); |
|||
source.LoadFrom(block.Data); |
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
Block8x8F temp = new Block8x8F(); |
|||
|
|||
source.IDCTInto(ref dest, ref temp); |
|||
dest.CopyTo(block.Data); |
|||
} |
|||
|
|||
internal static void SuchIDCT(ref BlockF block) |
|||
{ |
|||
Block8x8F source = new Block8x8F(); |
|||
source.LoadFrom(block.Data); |
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
Block8x8F temp = new Block8x8F(); |
|||
|
|||
source.IDCTInto(ref dest, ref temp); |
|||
dest.CopyTo(block.Data); |
|||
} |
|||
|
|||
public unsafe float this[int idx] |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
get |
|||
{ |
|||
fixed (Block8x8F* p = &this) |
|||
{ |
|||
float* fp = (float*) p; |
|||
return fp[idx]; |
|||
} |
|||
} |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
set |
|||
{ |
|||
fixed (Block8x8F* p = &this) |
|||
{ |
|||
float* fp = (float*) p; |
|||
fp[idx] = value; |
|||
} |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal static unsafe float GetScalarAt(Block8x8F* blockPtr, int idx) |
|||
{ |
|||
float* fp = (float*) blockPtr; |
|||
return fp[idx]; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal static unsafe void SetScalarAt(Block8x8F* blockPtr, int idx, float value) |
|||
{ |
|||
float* fp = (float*) blockPtr; |
|||
fp[idx] = value; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal void Clear() |
|||
{ |
|||
this = new Block8x8F(); // LOL C# Plz!
|
|||
} |
|||
|
|||
internal void LoadFrom(ref BlockF legacyBlock) |
|||
{ |
|||
LoadFrom(legacyBlock.Data); |
|||
} |
|||
|
|||
internal void CopyTo(ref BlockF legacyBlock) |
|||
{ |
|||
CopyTo(legacyBlock.Data); |
|||
} |
|||
|
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static byte ToColorByte(float c) |
|||
{ |
|||
if (c < -128) |
|||
{ |
|||
return 0; |
|||
} |
|||
else if (c > 127) |
|||
{ |
|||
return 255; |
|||
} |
|||
else |
|||
{ |
|||
c += 128; |
|||
return (byte) c; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
internal unsafe void CopyColorsTo(MutableSpan<byte> buffer, int stride) |
|||
{ |
|||
fixed (Block8x8F* p = &this) |
|||
{ |
|||
float* b = (float*) p; |
|||
|
|||
for (int y = 0; y < 8; y++) |
|||
{ |
|||
int y8 = y*8; |
|||
int yStride = y*stride; |
|||
|
|||
for (int x = 0; x < 8; x++) |
|||
{ |
|||
float c = b[y8 + x]; |
|||
|
|||
if (c < -128) |
|||
{ |
|||
c = 0; |
|||
} |
|||
else if (c > 127) |
|||
{ |
|||
c = 255; |
|||
} |
|||
else |
|||
{ |
|||
c += 128; |
|||
} |
|||
|
|||
buffer[yStride + x] = (byte) c; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
private static readonly Vector4 CMin4 = new Vector4(-128f); |
|||
private static readonly Vector4 CMax4 = new Vector4(127f); |
|||
private static readonly Vector4 COff4 = new Vector4(128f); |
|||
|
|||
/// <summary>
|
|||
/// Level shift by +128, clip to [0, 255], and write to buffer.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
internal unsafe void CopyColorsTo( |
|||
MutableSpan<byte> buffer, |
|||
int stride, |
|||
Block8x8F* temp) |
|||
{ |
|||
ColorifyInto(ref *temp); |
|||
|
|||
float* src = (float*) temp; |
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
buffer[0] = (byte) src[0]; |
|||
buffer[1] = (byte) src[1]; |
|||
buffer[2] = (byte) src[2]; |
|||
buffer[3] = (byte) src[3]; |
|||
buffer[4] = (byte) src[4]; |
|||
buffer[5] = (byte) src[5]; |
|||
buffer[6] = (byte) src[6]; |
|||
buffer[7] = (byte) src[7]; |
|||
buffer.AddOffset(stride); |
|||
src += 8; |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
using System.Buffers; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace ImageSharp.Formats |
|||
{ |
|||
/// <summary>
|
|||
/// Like corefxlab Span, but with an AddOffset() method for efficiency.
|
|||
/// TODO: When Span will be official, consider replacing this class!
|
|||
/// </summary>
|
|||
/// <see cref="https://github.com/dotnet/corefxlab/blob/master/src/System.Slices/System/Span.cs"/>
|
|||
/// <typeparam name="T"></typeparam>
|
|||
internal struct MutableSpan<T> |
|||
{ |
|||
public T[] Data; |
|||
public int Offset; |
|||
|
|||
public int TotalCount => Data.Length - Offset; |
|||
|
|||
public MutableSpan(int size, int offset = 0) |
|||
{ |
|||
Data = new T[size]; |
|||
Offset = offset; |
|||
} |
|||
|
|||
public MutableSpan(T[] data, int offset = 0) |
|||
{ |
|||
Data = data; |
|||
Offset = offset; |
|||
} |
|||
|
|||
public T this[int idx] |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Data[idx + Offset]; } |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] set { Data[idx + Offset] = value; } |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public MutableSpan<T> Slice(int offset) |
|||
{ |
|||
return new MutableSpan<T>(Data, Offset + offset); |
|||
} |
|||
|
|||
public static implicit operator MutableSpan<T>(T[] data) => new MutableSpan<T>(data, 0); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void AddOffset(int offset) |
|||
{ |
|||
Offset += offset; |
|||
} |
|||
} |
|||
|
|||
internal static class MutableSpanExtensions |
|||
{ |
|||
public static MutableSpan<T> Slice<T>(this T[] array, int offset) => new MutableSpan<T>(array, offset); |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void SaveTo(this MutableSpan<float> data, ref Vector4 v) |
|||
{ |
|||
v.X = data[0]; |
|||
v.Y = data[1]; |
|||
v.Z = data[2]; |
|||
v.W = data[3]; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void SaveTo(this MutableSpan<int> data, ref Vector4 v) |
|||
{ |
|||
v.X = data[0]; |
|||
v.Y = data[1]; |
|||
v.Z = data[2]; |
|||
v.W = data[3]; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void LoadFrom(this MutableSpan<float> data, ref Vector4 v) |
|||
{ |
|||
data[0] = v.X; |
|||
data[1] = v.Y; |
|||
data[2] = v.Z; |
|||
data[3] = v.W; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void LoadFrom(this MutableSpan<int> data, ref Vector4 v) |
|||
{ |
|||
data[0] = (int)v.X; |
|||
data[1] = (int)v.Y; |
|||
data[2] = (int)v.Z; |
|||
data[3] = (int)v.W; |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,511 @@ |
|||
// Uncomment this to turn unit tests into benchmarks:
|
|||
//#define BENCHMARKING
|
|||
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
using ImageSharp.Formats; |
|||
using Xunit; |
|||
using Xunit.Abstractions; |
|||
// ReSharper disable InconsistentNaming
|
|||
|
|||
namespace ImageSharp.Tests.Formats.Jpg |
|||
{ |
|||
public class Block8x8FTests : UtilityTestClassBase |
|||
{ |
|||
#if BENCHMARKING
|
|||
public const int Times = 1000000; |
|||
#else
|
|||
public const int Times = 1; |
|||
#endif
|
|||
|
|||
public Block8x8FTests(ITestOutputHelper output) : base(output) |
|||
{ |
|||
} |
|||
|
|||
[Fact] |
|||
public void Indexer() |
|||
{ |
|||
float sum = 0; |
|||
Measure(Times, () => |
|||
{ |
|||
Block8x8F block = new Block8x8F(); |
|||
|
|||
for (int i = 0; i < Block8x8F.ScalarCount; i++) |
|||
{ |
|||
block[i] = i; |
|||
} |
|||
sum = 0; |
|||
for (int i = 0; i < Block8x8F.ScalarCount; i++) |
|||
{ |
|||
sum += block[i]; |
|||
} |
|||
}); |
|||
Assert.Equal(sum, 64f*63f*0.5f); |
|||
} |
|||
|
|||
[Fact] |
|||
public unsafe void Indexer_GetScalarAt_SetScalarAt() |
|||
{ |
|||
float sum = 0; |
|||
Measure(Times, () => |
|||
{ |
|||
Block8x8F block = new Block8x8F(); |
|||
|
|||
for (int i = 0; i < Block8x8F.ScalarCount; i++) |
|||
{ |
|||
Block8x8F.SetScalarAt(&block, i, i); |
|||
} |
|||
sum = 0; |
|||
for (int i = 0; i < Block8x8F.ScalarCount; i++) |
|||
{ |
|||
sum += Block8x8F.GetScalarAt(&block, i); |
|||
} |
|||
}); |
|||
Assert.Equal(sum, 64f*63f*0.5f); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Indexer_ReferenceBenchmarkWithArray() |
|||
{ |
|||
float sum = 0; |
|||
|
|||
|
|||
Measure(Times, () => |
|||
{ |
|||
//Block8x8F block = new Block8x8F();
|
|||
float[] block = new float[64]; |
|||
for (int i = 0; i < Block8x8F.ScalarCount; i++) |
|||
{ |
|||
block[i] = i; |
|||
} |
|||
sum = 0; |
|||
for (int i = 0; i < Block8x8F.ScalarCount; i++) |
|||
{ |
|||
sum += block[i]; |
|||
} |
|||
}); |
|||
Assert.Equal(sum, 64f*63f*0.5f); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Load_Store_FloatArray() |
|||
{ |
|||
float[] data = new float[Block8x8F.ScalarCount]; |
|||
float[] mirror = new float[Block8x8F.ScalarCount]; |
|||
|
|||
for (int i = 0; i < Block8x8F.ScalarCount; i++) |
|||
{ |
|||
data[i] = i; |
|||
} |
|||
Measure(Times, () => |
|||
{ |
|||
Block8x8F b = new Block8x8F(); |
|||
b.LoadFrom(data); |
|||
b.CopyTo(mirror); |
|||
}); |
|||
|
|||
Assert.Equal(data, mirror); |
|||
//PrintLinearData((MutableSpan<float>)mirror);
|
|||
} |
|||
|
|||
[Fact] |
|||
public unsafe void Load_Store_FloatArray_Ptr() |
|||
{ |
|||
float[] data = new float[Block8x8F.ScalarCount]; |
|||
float[] mirror = new float[Block8x8F.ScalarCount]; |
|||
|
|||
for (int i = 0; i < Block8x8F.ScalarCount; i++) |
|||
{ |
|||
data[i] = i; |
|||
} |
|||
Measure(Times, () => |
|||
{ |
|||
Block8x8F b = new Block8x8F(); |
|||
Block8x8F.LoadFrom(&b, data); |
|||
Block8x8F.CopyTo(&b, mirror); |
|||
}); |
|||
|
|||
Assert.Equal(data, mirror); |
|||
//PrintLinearData((MutableSpan<float>)mirror);
|
|||
} |
|||
|
|||
[Fact] |
|||
public void Load_Store_IntArray() |
|||
{ |
|||
int[] data = new int[Block8x8F.ScalarCount]; |
|||
int[] mirror = new int[Block8x8F.ScalarCount]; |
|||
|
|||
for (int i = 0; i < Block8x8F.ScalarCount; i++) |
|||
{ |
|||
data[i] = i; |
|||
} |
|||
Measure(Times, () => |
|||
{ |
|||
Block8x8F v = new Block8x8F(); |
|||
v.LoadFrom(data); |
|||
v.CopyTo(mirror); |
|||
}); |
|||
|
|||
Assert.Equal(data, mirror); |
|||
//PrintLinearData((MutableSpan<int>)mirror);
|
|||
} |
|||
|
|||
[Fact] |
|||
public void TransposeInplace() |
|||
{ |
|||
float[] expected = Create8x8FloatData(); |
|||
ReferenceImplementations.Transpose8x8(expected); |
|||
|
|||
Block8x8F buffer = new Block8x8F(); |
|||
buffer.LoadFrom(Create8x8FloatData()); |
|||
|
|||
buffer.TransposeInplace(); |
|||
|
|||
float[] actual = new float[64]; |
|||
buffer.CopyTo(actual); |
|||
|
|||
Assert.Equal(expected, actual); |
|||
} |
|||
|
|||
[Fact] |
|||
public void TranposeInto_PinningImpl() |
|||
{ |
|||
float[] expected = Create8x8FloatData(); |
|||
ReferenceImplementations.Transpose8x8(expected); |
|||
|
|||
Block8x8F source = new Block8x8F(); |
|||
source.LoadFrom(Create8x8FloatData()); |
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
source.TransposeInto_PinningImpl(ref dest); |
|||
|
|||
float[] actual = new float[64]; |
|||
dest.CopyTo(actual); |
|||
|
|||
Assert.Equal(expected, actual); |
|||
} |
|||
|
|||
[Fact] |
|||
public void TransposeInto() |
|||
{ |
|||
float[] expected = Create8x8FloatData(); |
|||
ReferenceImplementations.Transpose8x8(expected); |
|||
|
|||
Block8x8F source = new Block8x8F(); |
|||
source.LoadFrom(Create8x8FloatData()); |
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
source.TransposeInto(ref dest); |
|||
|
|||
float[] actual = new float[64]; |
|||
dest.CopyTo(actual); |
|||
|
|||
Assert.Equal(expected, actual); |
|||
} |
|||
|
|||
[Fact] |
|||
public void TransposeInto_CodeGeneratorTest() |
|||
{ |
|||
char[] coordz = new[] {'X', 'Y', 'Z', 'W'}; |
|||
StringBuilder bld = new StringBuilder(); |
|||
|
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
char destCoord = coordz[i%4]; |
|||
char destSide = (i/4)%2 == 0 ? 'L' : 'R'; |
|||
|
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
char srcCoord = coordz[j%4]; |
|||
char srcSide = (j/4)%2 == 0 ? 'L' : 'R'; |
|||
|
|||
string expression = $"d.V{j}{destSide}.{destCoord} = V{i}{srcSide}.{srcCoord}; "; |
|||
bld.Append(expression); |
|||
} |
|||
bld.AppendLine(); |
|||
} |
|||
|
|||
Output.WriteLine(bld.ToString()); |
|||
} |
|||
|
|||
|
|||
[Fact] |
|||
public unsafe void TransposeInto_WithPointers() |
|||
{ |
|||
float[] expected = Create8x8FloatData(); |
|||
ReferenceImplementations.Transpose8x8(expected); |
|||
|
|||
Block8x8F source = new Block8x8F(); |
|||
source.LoadFrom(Create8x8FloatData()); |
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
|
|||
Block8x8F* sPtr = &source; |
|||
Block8x8F* dPtr = &dest; |
|||
|
|||
Block8x8F.TransposeInto(sPtr, dPtr); |
|||
|
|||
float[] actual = new float[64]; |
|||
dest.CopyTo(actual); |
|||
|
|||
Assert.Equal(expected, actual); |
|||
} |
|||
|
|||
private class BufferHolder |
|||
{ |
|||
public Block8x8F Buffer; |
|||
} |
|||
|
|||
[Fact] |
|||
public void TranposeInto_Benchmark() |
|||
{ |
|||
BufferHolder source = new BufferHolder(); |
|||
source.Buffer.LoadFrom(Create8x8FloatData()); |
|||
BufferHolder dest = new BufferHolder(); |
|||
|
|||
Output.WriteLine($"TranposeInto_PinningImpl_Benchmark X {Times} ..."); |
|||
Stopwatch sw = Stopwatch.StartNew(); |
|||
|
|||
for (int i = 0; i < Times; i++) |
|||
{ |
|||
source.Buffer.TransposeInto(ref dest.Buffer); |
|||
} |
|||
|
|||
sw.Stop(); |
|||
Output.WriteLine($"TranposeInto_PinningImpl_Benchmark finished in {sw.ElapsedMilliseconds} ms"); |
|||
|
|||
} |
|||
|
|||
[Fact] |
|||
public void TranposeInto_PinningImpl_Benchmark() |
|||
{ |
|||
BufferHolder source = new BufferHolder(); |
|||
source.Buffer.LoadFrom(Create8x8FloatData()); |
|||
BufferHolder dest = new BufferHolder(); |
|||
|
|||
Output.WriteLine($"TranposeInto_PinningImpl_Benchmark X {Times} ..."); |
|||
Stopwatch sw = Stopwatch.StartNew(); |
|||
|
|||
for (int i = 0; i < Times; i++) |
|||
{ |
|||
source.Buffer.TransposeInto_PinningImpl(ref dest.Buffer); |
|||
} |
|||
|
|||
sw.Stop(); |
|||
Output.WriteLine($"TranposeInto_PinningImpl_Benchmark finished in {sw.ElapsedMilliseconds} ms"); |
|||
} |
|||
|
|||
[Fact] |
|||
public unsafe void TransposeInto_WithPointers_Benchmark() |
|||
{ |
|||
BufferHolder source = new BufferHolder(); |
|||
source.Buffer.LoadFrom(Create8x8FloatData()); |
|||
BufferHolder dest = new BufferHolder(); |
|||
|
|||
fixed (Block8x8F* sPtr = &source.Buffer) |
|||
{ |
|||
fixed (Block8x8F* dPtr = &dest.Buffer) |
|||
{ |
|||
Output.WriteLine($"TransposeInto_WithPointers_Benchmark X {Times} ..."); |
|||
Stopwatch sw = Stopwatch.StartNew(); |
|||
|
|||
for (int i = 0; i < Times; i++) |
|||
{ |
|||
Block8x8F.TransposeInto(sPtr, dPtr); |
|||
} |
|||
|
|||
sw.Stop(); |
|||
Output.WriteLine($"TransposeInto_WithPointers_Benchmark finished in {sw.ElapsedMilliseconds} ms"); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
[Fact] |
|||
public void iDCT2D8x4_LeftPart() |
|||
{ |
|||
float[] sourceArray = Create8x8FloatData(); |
|||
float[] expectedDestArray = new float[64]; |
|||
|
|||
ReferenceImplementations.iDCT2D8x4_32f(sourceArray, expectedDestArray); |
|||
|
|||
Block8x8F source = new Block8x8F(); |
|||
source.LoadFrom(sourceArray); |
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
|
|||
source.iDCT2D8x4_LeftPart(ref dest); |
|||
|
|||
float[] actualDestArray = new float[64]; |
|||
dest.CopyTo(actualDestArray); |
|||
|
|||
Print8x8Data(expectedDestArray); |
|||
Output.WriteLine("**************"); |
|||
Print8x8Data(actualDestArray); |
|||
|
|||
Assert.Equal(expectedDestArray, actualDestArray); |
|||
} |
|||
|
|||
[Fact] |
|||
public void iDCT2D8x4_RightPart() |
|||
{ |
|||
MutableSpan<float> sourceArray = Create8x8FloatData(); |
|||
MutableSpan<float> expectedDestArray = new float[64]; |
|||
|
|||
ReferenceImplementations.iDCT2D8x4_32f(sourceArray.Slice(4), expectedDestArray.Slice(4)); |
|||
|
|||
Block8x8F source = new Block8x8F(); |
|||
source.LoadFrom(sourceArray); |
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
|
|||
source.iDCT2D8x4_RightPart(ref dest); |
|||
|
|||
float[] actualDestArray = new float[64]; |
|||
dest.CopyTo(actualDestArray); |
|||
|
|||
Print8x8Data(expectedDestArray); |
|||
Output.WriteLine("**************"); |
|||
Print8x8Data(actualDestArray); |
|||
|
|||
Assert.Equal(expectedDestArray.Data, actualDestArray); |
|||
} |
|||
|
|||
private struct ApproximateFloatComparer : IEqualityComparer<float> |
|||
{ |
|||
private const float Eps = 0.0001f; |
|||
|
|||
public bool Equals(float x, float y) |
|||
{ |
|||
float d = x - y; |
|||
|
|||
return d > -Eps && d < Eps; |
|||
} |
|||
|
|||
public int GetHashCode(float obj) |
|||
{ |
|||
throw new InvalidOperationException(); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void IDCTInto() |
|||
{ |
|||
float[] sourceArray = Create8x8FloatData(); |
|||
float[] expectedDestArray = new float[64]; |
|||
float[] tempArray = new float[64]; |
|||
|
|||
ReferenceImplementations.iDCT2D_llm(sourceArray, expectedDestArray, tempArray); |
|||
|
|||
//ReferenceImplementations.iDCT8x8_llm_sse(sourceArray, expectedDestArray, tempArray);
|
|||
|
|||
Block8x8F source = new Block8x8F(); |
|||
source.LoadFrom(sourceArray); |
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
Block8x8F tempBuffer = new Block8x8F(); |
|||
|
|||
source.IDCTInto(ref dest, ref tempBuffer); |
|||
|
|||
float[] actualDestArray = new float[64]; |
|||
dest.CopyTo(actualDestArray); |
|||
|
|||
Print8x8Data(expectedDestArray); |
|||
Output.WriteLine("**************"); |
|||
Print8x8Data(actualDestArray); |
|||
Assert.Equal(expectedDestArray, actualDestArray, new ApproximateFloatComparer()); |
|||
Assert.Equal(expectedDestArray, actualDestArray, new ApproximateFloatComparer()); |
|||
} |
|||
|
|||
|
|||
[Fact] |
|||
public unsafe void CopyColorsTo() |
|||
{ |
|||
var data = Create8x8FloatData(); |
|||
Block8x8F block = new Block8x8F(); |
|||
block.LoadFrom(data); |
|||
block.MultiplyAllInplace(new Vector4(5, 5, 5, 5)); |
|||
|
|||
int stride = 256; |
|||
int height = 42; |
|||
int offset = height*10 + 20; |
|||
|
|||
byte[] colorsExpected = new byte[stride*height]; |
|||
byte[] colorsActual = new byte[stride*height]; |
|||
|
|||
Block8x8F temp = new Block8x8F(); |
|||
|
|||
ReferenceImplementations.CopyColorsTo(ref block, new MutableSpan<byte>(colorsExpected, offset), stride); |
|||
|
|||
block.CopyColorsTo(new MutableSpan<byte>(colorsActual, offset), stride, &temp); |
|||
|
|||
//Output.WriteLine("******* EXPECTED: *********");
|
|||
//PrintLinearData(colorsExpected);
|
|||
//Output.WriteLine("******** ACTUAL: **********");
|
|||
|
|||
Assert.Equal(colorsExpected, colorsActual); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CropInto() |
|||
{ |
|||
Block8x8F block = new Block8x8F(); |
|||
block.LoadFrom(Create8x8FloatData()); |
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
block.CropInto(10, 20, ref dest); |
|||
|
|||
float[] array = new float[64]; |
|||
dest.CopyTo(array); |
|||
PrintLinearData(array); |
|||
foreach (float val in array) |
|||
{ |
|||
Assert.InRange(val, 10, 20); |
|||
} |
|||
|
|||
} |
|||
|
|||
private static float[] Create8x8ColorCropTestData() |
|||
{ |
|||
float[] result = new float[64]; |
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
result[i * 8 + j] = -300 + i * 100 + j * 10; |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
[Fact] |
|||
public void ColorifyInto() |
|||
{ |
|||
Block8x8F block = new Block8x8F(); |
|||
var input = Create8x8ColorCropTestData(); |
|||
block.LoadFrom(input); |
|||
Output.WriteLine("Input:"); |
|||
PrintLinearData(input); |
|||
|
|||
|
|||
Block8x8F dest = new Block8x8F(); |
|||
block.ColorifyInto(ref dest); |
|||
|
|||
float[] array = new float[64]; |
|||
dest.CopyTo(array); |
|||
Output.WriteLine("Result:"); |
|||
PrintLinearData(array); |
|||
foreach (float val in array) |
|||
{ |
|||
Assert.InRange(val, 0, 255); |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using System.Numerics; |
|||
using ImageSharp.Formats; |
|||
using Xunit; |
|||
using Xunit.Abstractions; |
|||
|
|||
namespace ImageSharp.Tests.Formats.Jpg |
|||
{ |
|||
public class DctTests : UtilityTestClassBase |
|||
{ |
|||
public DctTests(ITestOutputHelper output) |
|||
: base(output) |
|||
{ |
|||
} |
|||
|
|||
[Fact] |
|||
public void Mennyi() |
|||
{ |
|||
Output.WriteLine(Vector.IsHardwareAccelerated.ToString()); |
|||
Output.WriteLine(Vector<float>.Count.ToString()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void CheckTestData() |
|||
{ |
|||
var data = Create8x8FloatData(); |
|||
|
|||
Print8x8Data(data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Transpose8x8() |
|||
{ |
|||
var data = Create8x8FloatData(); |
|||
|
|||
MutableSpan<float> result = new MutableSpan<float>(64); |
|||
|
|||
ReferenceImplementations.Transpose8x8(data, result); |
|||
|
|||
Print8x8Data(result.Data); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Transpose8x8_Inplace() |
|||
{ |
|||
var data = Create8x8FloatData(); |
|||
|
|||
ReferenceImplementations.Transpose8x8(data); |
|||
|
|||
Print8x8Data(data); |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,364 @@ |
|||
using System; |
|||
using System.Buffers; |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
using ImageSharp.Formats; |
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
|
|||
namespace ImageSharp.Tests.Formats.Jpg |
|||
{ |
|||
/// <summary>
|
|||
/// This class contains simplified (unefficient) reference implementations so we can verify actual ones in unit tests
|
|||
/// DCT code Ported from https://github.com/norishigefukushima/dct_simd
|
|||
/// </summary>
|
|||
public static class ReferenceImplementations |
|||
{ |
|||
internal static void Transpose8x8(MutableSpan<float> data) |
|||
{ |
|||
for (int i = 1; i < 8; i++) |
|||
{ |
|||
int i8 = i*8; |
|||
for (int j = 0; j < i; j++) |
|||
{ |
|||
float tmp = data[i8 + j]; |
|||
data[i8 + j] = data[j*8 + i]; |
|||
data[j*8 + i] = tmp; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal static void Transpose8x8(MutableSpan<float> src, MutableSpan<float> dest) |
|||
{ |
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
int i8 = i*8; |
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
dest[j*8 + i] = src[i8 + j]; |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal static void iDCT1Dllm_32f(MutableSpan<float> y, MutableSpan<float> x) |
|||
{ |
|||
float a0, a1, a2, a3, b0, b1, b2, b3; |
|||
float z0, z1, z2, z3, z4; |
|||
|
|||
float r0 = 1.414214f; |
|||
float r1 = 1.387040f; |
|||
float r2 = 1.306563f; |
|||
float r3 = 1.175876f; |
|||
float r4 = 1.000000f; |
|||
float r5 = 0.785695f; |
|||
float r6 = 0.541196f; |
|||
float r7 = 0.275899f; |
|||
|
|||
z0 = y[1] + y[7]; |
|||
z1 = y[3] + y[5]; |
|||
z2 = y[3] + y[7]; |
|||
z3 = y[1] + y[5]; |
|||
z4 = (z0 + z1)*r3; |
|||
|
|||
z0 = z0*(-r3 + r7); |
|||
z1 = z1*(-r3 - r1); |
|||
z2 = z2*(-r3 - r5) + z4; |
|||
z3 = z3*(-r3 + r5) + z4; |
|||
|
|||
b3 = y[7]*(-r1 + r3 + r5 - r7) + z0 + z2; |
|||
b2 = y[5]*(r1 + r3 - r5 + r7) + z1 + z3; |
|||
b1 = y[3]*(r1 + r3 + r5 - r7) + z1 + z2; |
|||
b0 = y[1]*(r1 + r3 - r5 - r7) + z0 + z3; |
|||
|
|||
z4 = (y[2] + y[6])*r6; |
|||
z0 = y[0] + y[4]; |
|||
z1 = y[0] - y[4]; |
|||
z2 = z4 - y[6]*(r2 + r6); |
|||
z3 = z4 + y[2]*(r2 - r6); |
|||
a0 = z0 + z3; |
|||
a3 = z0 - z3; |
|||
a1 = z1 + z2; |
|||
a2 = z1 - z2; |
|||
|
|||
x[0] = a0 + b0; |
|||
x[7] = a0 - b0; |
|||
x[1] = a1 + b1; |
|||
x[6] = a1 - b1; |
|||
x[2] = a2 + b2; |
|||
x[5] = a2 - b2; |
|||
x[3] = a3 + b3; |
|||
x[4] = a3 - b3; |
|||
} |
|||
|
|||
internal static void iDCT2D_llm(MutableSpan<float> s, MutableSpan<float> d, MutableSpan<float> temp) |
|||
{ |
|||
int j; |
|||
|
|||
for (j = 0; j < 8; j++) |
|||
{ |
|||
iDCT1Dllm_32f(s.Slice(j*8), temp.Slice(j*8)); |
|||
} |
|||
|
|||
Transpose8x8(temp, d); |
|||
|
|||
for (j = 0; j < 8; j++) |
|||
{ |
|||
iDCT1Dllm_32f(d.Slice(j*8), temp.Slice(j*8)); |
|||
} |
|||
|
|||
Transpose8x8(temp, d); |
|||
|
|||
for (j = 0; j < 64; j++) |
|||
{ |
|||
d[j] *= 0.125f; |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector4 _mm_load_ps(MutableSpan<float> src, int offset) |
|||
{ |
|||
src = src.Slice(offset); |
|||
return new Vector4(src[0], src[1], src[2], src[3]); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector4 _mm_load_ps(MutableSpan<float> src) |
|||
{ |
|||
return new Vector4(src[0], src[1], src[2], src[3]); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void _mm_store_ps(MutableSpan<float> dest, int offset, Vector4 src) |
|||
{ |
|||
dest = dest.Slice(offset); |
|||
dest[0] = src.X; |
|||
dest[1] = src.Y; |
|||
dest[2] = src.Z; |
|||
dest[3] = src.W; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static void _mm_store_ps(MutableSpan<float> dest, Vector4 src) |
|||
{ |
|||
dest[0] = src.X; |
|||
dest[1] = src.Y; |
|||
dest[2] = src.Z; |
|||
dest[3] = src.W; |
|||
} |
|||
|
|||
|
|||
private static readonly Vector4 _1_175876 = new Vector4(1.175876f); |
|||
private static readonly Vector4 _1_961571 = new Vector4(-1.961571f); |
|||
private static readonly Vector4 _0_390181 = new Vector4(-0.390181f); |
|||
private static readonly Vector4 _0_899976 = new Vector4(-0.899976f); |
|||
private static readonly Vector4 _2_562915 = new Vector4(-2.562915f); |
|||
private static readonly Vector4 _0_298631 = new Vector4(0.298631f); |
|||
private static readonly Vector4 _2_053120 = new Vector4(2.053120f); |
|||
private static readonly Vector4 _3_072711 = new Vector4(3.072711f); |
|||
private static readonly Vector4 _1_501321 = new Vector4(1.501321f); |
|||
private static readonly Vector4 _0_541196 = new Vector4(0.541196f); |
|||
private static readonly Vector4 _1_847759 = new Vector4(-1.847759f); |
|||
private static readonly Vector4 _0_765367 = new Vector4(0.765367f); |
|||
|
|||
internal static void iDCT2D8x4_32f(MutableSpan<float> y, MutableSpan<float> x) |
|||
{ |
|||
/* |
|||
float a0,a1,a2,a3,b0,b1,b2,b3; float z0,z1,z2,z3,z4; float r[8]; int i; |
|||
for(i = 0;i < 8;i++){ r[i] = (float)(cos((double)i / 16.0 * M_PI) * M_SQRT2); } |
|||
*/ |
|||
/* |
|||
0: 1.414214 |
|||
1: 1.387040 |
|||
2: 1.306563 |
|||
3: |
|||
4: 1.000000 |
|||
5: 0.785695 |
|||
6: |
|||
7: 0.275899 |
|||
*/ |
|||
|
|||
Vector4 my1 = _mm_load_ps(y, 8); |
|||
Vector4 my7 = _mm_load_ps(y, 56); |
|||
Vector4 mz0 = my1 + my7; |
|||
|
|||
Vector4 my3 = _mm_load_ps(y, 24); |
|||
Vector4 mz2 = my3 + my7; |
|||
Vector4 my5 = _mm_load_ps(y, 40); |
|||
Vector4 mz1 = my3 + my5; |
|||
Vector4 mz3 = my1 + my5; |
|||
|
|||
Vector4 mz4 = ((mz0 + mz1)* _1_175876); |
|||
//z0 = y[1] + y[7]; z1 = y[3] + y[5]; z2 = y[3] + y[7]; z3 = y[1] + y[5];
|
|||
//z4 = (z0 + z1) * r[3];
|
|||
|
|||
mz2 = mz2* _1_961571 + mz4; |
|||
mz3 = mz3* _0_390181 + mz4; |
|||
mz0 = mz0* _0_899976; |
|||
mz1 = mz1* _2_562915; |
|||
|
|||
/* |
|||
-0.899976 |
|||
-2.562915 |
|||
-1.961571 |
|||
-0.390181 |
|||
z0 = z0 * (-r[3] + r[7]); |
|||
z1 = z1 * (-r[3] - r[1]); |
|||
z2 = z2 * (-r[3] - r[5]) + z4; |
|||
z3 = z3 * (-r[3] + r[5]) + z4;*/ |
|||
|
|||
|
|||
Vector4 mb3 = my7* _0_298631 + mz0 + mz2; |
|||
Vector4 mb2 = my5* _2_053120 + mz1 + mz3; |
|||
Vector4 mb1 = my3* _3_072711 + mz1 + mz2; |
|||
Vector4 mb0 = my1* _1_501321 + mz0 + mz3; |
|||
|
|||
/* |
|||
0.298631 |
|||
2.053120 |
|||
3.072711 |
|||
1.501321 |
|||
b3 = y[7] * (-r[1] + r[3] + r[5] - r[7]) + z0 + z2; |
|||
b2 = y[5] * ( r[1] + r[3] - r[5] + r[7]) + z1 + z3; |
|||
b1 = y[3] * ( r[1] + r[3] + r[5] - r[7]) + z1 + z2; |
|||
b0 = y[1] * ( r[1] + r[3] - r[5] - r[7]) + z0 + z3; |
|||
*/ |
|||
|
|||
Vector4 my2 = _mm_load_ps(y, 16); |
|||
Vector4 my6 = _mm_load_ps(y, 48); |
|||
mz4 = (my2 + my6)* _0_541196; |
|||
Vector4 my0 = _mm_load_ps(y, 0); |
|||
Vector4 my4 = _mm_load_ps(y, 32); |
|||
mz0 = my0 + my4; |
|||
mz1 = my0 - my4; |
|||
|
|||
mz2 = mz4 + my6* _1_847759; |
|||
mz3 = mz4 + my2* _0_765367; |
|||
|
|||
my0 = mz0 + mz3; |
|||
my3 = mz0 - mz3; |
|||
my1 = mz1 + mz2; |
|||
my2 = mz1 - mz2; |
|||
/* |
|||
1.847759 |
|||
0.765367 |
|||
z4 = (y[2] + y[6]) * r[6]; |
|||
z0 = y[0] + y[4]; z1 = y[0] - y[4]; |
|||
z2 = z4 - y[6] * (r[2] + r[6]); |
|||
z3 = z4 + y[2] * (r[2] - r[6]); |
|||
a0 = z0 + z3; a3 = z0 - z3; |
|||
a1 = z1 + z2; a2 = z1 - z2; |
|||
*/ |
|||
|
|||
_mm_store_ps(x, 0, my0 + mb0); |
|||
|
|||
_mm_store_ps(x, 56, my0 - mb0); |
|||
|
|||
_mm_store_ps(x, 8, my1 + mb1); |
|||
|
|||
_mm_store_ps(x, 48, my1 - mb1); |
|||
|
|||
_mm_store_ps(x, 16, my2 + mb2); |
|||
|
|||
_mm_store_ps(x, 40, my2 - mb2); |
|||
|
|||
_mm_store_ps(x, 24, my3 + mb3); |
|||
|
|||
_mm_store_ps(x, 32, my3 - mb3); |
|||
/* |
|||
x[0] = a0 + b0; x[7] = a0 - b0; |
|||
x[1] = a1 + b1; x[6] = a1 - b1; |
|||
x[2] = a2 + b2; x[5] = a2 - b2; |
|||
x[3] = a3 + b3; x[4] = a3 - b3; |
|||
for(i = 0;i < 8;i++){ x[i] *= 0.353554f; } |
|||
*/ |
|||
} |
|||
|
|||
internal static void iDCT8x8_llm_sse(MutableSpan<float> s, MutableSpan<float> d, MutableSpan<float> temp) |
|||
{ |
|||
Transpose8x8(s, temp); |
|||
iDCT2D8x4_32f(temp, d); |
|||
|
|||
iDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); |
|||
|
|||
Transpose8x8(d, temp); |
|||
|
|||
iDCT2D8x4_32f(temp, d); |
|||
|
|||
iDCT2D8x4_32f(temp.Slice(4), d.Slice(4)); |
|||
|
|||
Vector4 c = new Vector4(0.1250f); |
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//0
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//1
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//2
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//3
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//4
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//5
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//6
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//7
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//8
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//9
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//10
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//11
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//12
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//13
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//14
|
|||
|
|||
_mm_store_ps(d, (_mm_load_ps(d)*c));d.AddOffset(4);//15
|
|||
} |
|||
|
|||
|
|||
internal static unsafe void CopyColorsTo(ref Block8x8F block, MutableSpan<byte> buffer, int stride) |
|||
{ |
|||
fixed (Block8x8F* p = &block) |
|||
{ |
|||
float* b = (float*)p; |
|||
|
|||
for (int y = 0; y < 8; y++) |
|||
{ |
|||
int y8 = y * 8; |
|||
int yStride = y * stride; |
|||
|
|||
for (int x = 0; x < 8; x++) |
|||
{ |
|||
float c = b[y8 + x]; |
|||
|
|||
if (c < -128) |
|||
{ |
|||
c = 0; |
|||
} |
|||
else if (c > 127) |
|||
{ |
|||
c = 255; |
|||
} |
|||
else |
|||
{ |
|||
c += 128; |
|||
} |
|||
|
|||
buffer[yStride + x] = (byte)c; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,95 @@ |
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Text; |
|||
using ImageSharp.Formats; |
|||
using Xunit.Abstractions; |
|||
|
|||
namespace ImageSharp.Tests.Formats.Jpg |
|||
{ |
|||
public class UtilityTestClassBase |
|||
{ |
|||
public UtilityTestClassBase(ITestOutputHelper output) |
|||
{ |
|||
Output = output; |
|||
} |
|||
|
|||
protected ITestOutputHelper Output { get; } |
|||
|
|||
// ReSharper disable once InconsistentNaming
|
|||
public static float[] Create8x8FloatData() |
|||
{ |
|||
float[] result = new float[64]; |
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
result[i * 8 + j] = i * 10 + j; |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
|
|||
|
|||
|
|||
// ReSharper disable once InconsistentNaming
|
|||
public static int[] Create8x8IntData() |
|||
{ |
|||
int[] result = new int[64]; |
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
result[i * 8 + j] = i * 10 + j; |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
internal void Print8x8Data<T>(MutableSpan<T> data) => Print8x8Data(data.Data); |
|||
|
|||
internal void Print8x8Data<T>(T[] data) |
|||
{ |
|||
StringBuilder bld = new StringBuilder(); |
|||
for (int i = 0; i < 8; i++) |
|||
{ |
|||
for (int j = 0; j < 8; j++) |
|||
{ |
|||
bld.Append($"{data[i * 8 + j],3} "); |
|||
} |
|||
bld.AppendLine(); |
|||
} |
|||
|
|||
Output.WriteLine(bld.ToString()); |
|||
} |
|||
|
|||
internal void PrintLinearData<T>(T[] data) => PrintLinearData(new MutableSpan<T>(data), data.Length); |
|||
|
|||
internal void PrintLinearData<T>(MutableSpan<T> data, int count = -1) |
|||
{ |
|||
if (count < 0) count = data.TotalCount; |
|||
|
|||
StringBuilder bld = new StringBuilder(); |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
bld.Append($"{data[i],3} "); |
|||
} |
|||
Output.WriteLine(bld.ToString()); |
|||
} |
|||
|
|||
protected void Measure(int times, Action action, [CallerMemberName] string operationName = null) |
|||
{ |
|||
Output.WriteLine($"{operationName} X {times} ..."); |
|||
Stopwatch sw = Stopwatch.StartNew(); |
|||
|
|||
for (int i = 0; i < times; i++) |
|||
{ |
|||
action(); |
|||
} |
|||
|
|||
sw.Stop(); |
|||
Output.WriteLine($"{operationName} finished in {sw.ElapsedMilliseconds} ms"); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue