diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings
index b058fad4ed..1839bf2f8d 100644
--- a/ImageSharp.sln.DotSettings
+++ b/ImageSharp.sln.DotSettings
@@ -342,6 +342,7 @@
True
AC
DC
+ DCT
EOF
FDCT
IDCT
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
index 51016c8288..0a15c37f01 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
@@ -44,6 +44,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
}
}
+ public short this[int y, int x]
+ {
+ get => this[(y * 8) + x];
+ set => this[(y * 8) + x] = value;
+ }
+
public static bool operator ==(Block8x8 left, Block8x8 right)
{
return left.Equals(right);
@@ -83,9 +89,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
short* fp = blockPtr->data;
fp[idx] = value;
}
-
- public short GetValueAt(int x, int y) => this[(y * 8) + x];
-
+
public Block8x8F AsFloatBlock()
{
// TODO: Optimize this
@@ -112,6 +116,22 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
Unsafe.CopyBlock(ref destRef, ref selfRef, Size * sizeof(short));
}
+ public void CopyTo(Span destination)
+ {
+ for (int i = 0; i < Size; i++)
+ {
+ destination[i] = this[i];
+ }
+ }
+
+ public void LoadFrom(Span source)
+ {
+ for (int i = 0; i < Size; i++)
+ {
+ this[i] = (short)source[i];
+ }
+ }
+
[Conditional("DEBUG")]
private static void GuardBlockIndex(int idx)
{
diff --git a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
index 491908f92a..b3220dea69 100644
--- a/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
+++ b/src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
@@ -83,6 +83,12 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
}
}
+ public float this[int y, int x]
+ {
+ get => this[(y * 8) + x];
+ set => this[(y * 8) + x] = value;
+ }
+
///
/// Pointer-based "Indexer" (getter part)
///
@@ -113,17 +119,17 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
fp[idx] = value;
}
- public Block8x8 AsInt16Block()
- {
- // TODO: Optimize this
- var result = default(Block8x8);
- for (int i = 0; i < Size; i++)
- {
- result[i] = (short)this[i];
- }
+ //public Block8x8 AsInt16Block()
+ //{
+ // // TODO: Optimize this
+ // var result = default(Block8x8);
+ // for (int i = 0; i < Size; i++)
+ // {
+ // result[i] = (short)this[i];
+ // }
- return result;
- }
+ // return result;
+ //}
///
/// Fill the block with defaults (zeroes)
@@ -413,6 +419,30 @@ namespace SixLabors.ImageSharp.Formats.Jpeg.Common
a.V7R = DivideRound(a.V7R, b.V7R);
}
+ public void RoundInto(ref Block8x8 dest)
+ {
+ for (int i = 0; i < Size; i++)
+ {
+ float val = this[i];
+ if (val < 0)
+ {
+ val -= 0.5f;
+ }
+ else
+ {
+ val += 0.5f;
+ }
+ dest[i] = (short)val;
+ }
+ }
+
+ public Block8x8 RoundAsInt16Block()
+ {
+ var result = default(Block8x8);
+ this.RoundInto(ref result);
+ return result;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Vector4 DivideRound(Vector4 dividend, Vector4 divisor)
{
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
index 15609991d2..edf3162d27 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
@@ -291,10 +291,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
public unsafe void UnzigDivRound(int seed)
{
Block8x8F block = new Block8x8F();
- block.LoadFrom(Create8x8RandomFloatData(-2000, 2000, seed));
+ block.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed));
Block8x8F qt = new Block8x8F();
- qt.LoadFrom(Create8x8RandomFloatData(-2000, 2000, seed));
+ qt.LoadFrom(Create8x8RoundedRandomFloatData(-2000, 2000, seed));
UnzigData unzig = UnzigData.Create();
@@ -314,19 +314,40 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
}
+ //[Fact]
+ //public void AsInt16Block()
+ //{
+ // float[] data = Create8x8FloatData();
+
+ // var source = default(Block8x8F);
+ // source.LoadFrom(data);
+
+ // Block8x8 dest = source.AsInt16Block();
+
+ // for (int i = 0; i < Block8x8F.Size; i++)
+ // {
+ // Assert.Equal((short)data[i], dest[i]);
+ // }
+ //}
+
[Fact]
- public void AsInt16Block()
+ public void RoundInto()
{
- float[] data = Create8x8FloatData();
+ float[] data = Create8x8RandomFloatData(-1000, 1000);
var source = default(Block8x8F);
source.LoadFrom(data);
+ var dest = default(Block8x8);
- Block8x8 dest = source.AsInt16Block();
+ source.RoundInto(ref dest);
- for (int i = 0; i < Block8x8F.Size; i++)
+ for (int i = 0; i < Block8x8.Size; i++)
{
- Assert.Equal((short)data[i], dest[i]);
+ float expectedFloat = data[i];
+ short expectedShort = (short) Math.Round(expectedFloat);
+ short actualShort = dest[i];
+
+ Assert.Equal(expectedShort, actualShort);
}
}
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
index 45096a8b6a..8c1d5fb906 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
@@ -115,12 +115,12 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
}
[Fact]
- public void GetValueAt()
+ public void IndexerYX()
{
var block = default(Block8x8);
block[8 * 3 + 5] = 42;
- short value = block.GetValueAt(5, 3);
+ short value = block[3, 5];
Assert.Equal(42, value);
}
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
index 562027b88c..8c9c4bbca3 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
@@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(3)]
public void TransformIDCT(int seed)
{
- Span sourceArray = JpegUtilityTestFixture.Create8x8RandomFloatData(-200, 200, seed);
+ Span sourceArray = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
float[] expectedDestArray = new float[64];
float[] tempArray = new float[64];
@@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)]
public void FDCT8x4_LeftPart(int seed)
{
- Span src = JpegUtilityTestFixture.Create8x8RandomFloatData(-200, 200, seed);
+ Span src = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src);
@@ -129,7 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)]
public void FDCT8x4_RightPart(int seed)
{
- Span src = JpegUtilityTestFixture.Create8x8RandomFloatData(-200, 200, seed);
+ Span src = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src);
@@ -151,7 +151,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg
[InlineData(2)]
public void TransformFDCT(int seed)
{
- Span src = JpegUtilityTestFixture.Create8x8RandomFloatData(-200, 200, seed);
+ Span src = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src);
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs
new file mode 100644
index 0000000000..4d7ad8a7e1
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs
@@ -0,0 +1,113 @@
+// ReSharper disable InconsistentNaming
+namespace SixLabors.ImageSharp.Tests.Formats.Jpg
+{
+ using System;
+
+ using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
+
+ using Xunit;
+
+ public partial class ReferenceImplementationsTests
+ {
+ public class FastFloatingPointDCT
+ {
+ [Theory]
+ [InlineData(42, 0)]
+ [InlineData(1, 0)]
+ [InlineData(2, 0)]
+ public void ForwardThenInverse(int seed, int startAt)
+ {
+ int[] data = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
+ float[] src = data.ConvertAllToFloat();
+ float[] dest = new float[64];
+ float[] temp = new float[64];
+
+ ReferenceImplementations.FastFloatingPointDCT.fDCT2D_llm(src, dest, temp, true);
+ ReferenceImplementations.FastFloatingPointDCT.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));
+ }
+ }
+
+ [Theory]
+ [InlineData(42)]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void IDCT_IsEquivalentTo_StandardIntegerImplementation(int seed)
+ {
+ int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
+ Span floatSrc = intData.ConvertAllToFloat();
+
+ ReferenceImplementations.StandardIntegerDCT.TransformIDCTInplace(intData);
+
+ float[] dest = new float[64];
+ float[] temp = new float[64];
+
+ ReferenceImplementations.FastFloatingPointDCT.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)]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void IDCT_IsEquivalentTo_AccurateImplementation(int seed)
+ {
+ int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
+ float[] floatSrc = intData.ConvertAllToFloat();
+
+ ReferenceImplementations.AccurateDCT.TransformIDCTInplace(intData);
+
+ float[] dest = new float[64];
+ float[] temp = new float[64];
+
+ ReferenceImplementations.FastFloatingPointDCT.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)]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void FDCT_IsEquivalentTo_StandardIntegerImplementation(int seed)
+ {
+ int[] intData = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
+ float[] floatSrc = intData.ConvertAllToFloat();
+
+ ReferenceImplementations.StandardIntegerDCT.TransformFDCTInplace(intData);
+
+ float[] dest = new float[64];
+ float[] temp = new float[64];
+
+ ReferenceImplementations.FastFloatingPointDCT.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));
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs
new file mode 100644
index 0000000000..49187d9bc8
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs
@@ -0,0 +1,73 @@
+namespace SixLabors.ImageSharp.Tests.Formats.Jpg
+{
+ using System;
+
+ using SixLabors.ImageSharp.Formats.Jpeg.Common;
+ using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
+
+ using Xunit;
+ using Xunit.Abstractions;
+
+ public partial class ReferenceImplementationsTests
+ {
+
+ public class StandardIntegerDCT
+ {
+ public StandardIntegerDCT(ITestOutputHelper output)
+ {
+ this.Output = output;
+ }
+
+ private ITestOutputHelper Output { get; }
+
+ [Theory]
+ [InlineData(42)]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void IDCT_IsEquivalentTo_AccurateImplementation(int seed)
+ {
+ int[] data = Create8x8RandomIntData(-1000, 1000, seed);
+
+ Block8x8 source = default(Block8x8);
+ source.LoadFrom(data);
+
+ Block8x8 expected = ReferenceImplementations.AccurateDCT.TransformIDCT(ref source);
+ Block8x8 actual = ReferenceImplementations.StandardIntegerDCT.TransformIDCT(ref source);
+
+ long diff = Block8x8.TotalDifference(ref expected, ref actual);
+ this.Output.WriteLine(expected.ToString());
+ this.Output.WriteLine(actual.ToString());
+ this.Output.WriteLine("DIFFERENCE: "+diff);
+ }
+
+
+ [Theory]
+ [InlineData(42, 0)]
+ [InlineData(1, 0)]
+ [InlineData(2, 0)]
+ public void ForwardThenInverse(int seed, int startAt)
+ {
+ Span original = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
+
+ Span block = original.AddScalarToAllValues(128);
+
+ ReferenceImplementations.StandardIntegerDCT.TransformFDCTInplace(block);
+
+ for (int i = 0; i < 64; i++)
+ {
+ block[i] /= 8;
+ }
+
+ ReferenceImplementations.StandardIntegerDCT.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));
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
index fc9e84cecd..8b97f1208e 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
@@ -3,124 +3,18 @@
using SixLabors.ImageSharp.Formats.Jpeg.GolangPort.Utils;
-using Xunit;
using Xunit.Abstractions;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Jpg
{
- using System;
-
using SixLabors.ImageSharp.Tests.Formats.Jpg.Utils;
- public class ReferenceImplementationsTests : JpegUtilityTestFixture
+ public partial class ReferenceImplementationsTests : JpegUtilityTestFixture
{
public ReferenceImplementationsTests(ITestOutputHelper output)
: base(output)
{
}
-
-
- [Theory]
- [InlineData(42)]
- [InlineData(1)]
- [InlineData(2)]
- public void Idct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed)
- {
- int[] intData = Create8x8RandomIntData(-200, 200, seed);
- Span floatSrc = intData.ConvertAllToFloat();
-
- ReferenceImplementations.IntegerDCT.TransformIDCTInplace(intData);
-
- float[] dest = new float[64];
- float[] temp = new float[64];
-
- ReferenceImplementations.FastFloatingPointDCT.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)
- {
- Span original = Create8x8RandomIntData(-200, 200, seed);
-
- Span block = original.AddScalarToAllValues(128);
-
- ReferenceImplementations.IntegerDCT.TransformFDCTInplace(block);
-
- for (int i = 0; i < 64; i++)
- {
- block[i] /= 8;
- }
-
- ReferenceImplementations.IntegerDCT.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)
- {
- int[] data = Create8x8RandomIntData(-200, 200, seed);
- float[] src = data.ConvertAllToFloat();
- float[] dest = new float[64];
- float[] temp = new float[64];
-
- ReferenceImplementations.FastFloatingPointDCT.fDCT2D_llm(src, dest, temp, true);
- ReferenceImplementations.FastFloatingPointDCT.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));
- }
- }
-
- [Theory]
- [InlineData(42)]
- [InlineData(1)]
- [InlineData(2)]
- public void Fdct_FloatingPointReferenceImplementation_IsEquivalentToIntegerImplementation(int seed)
- {
- int[] intData = Create8x8RandomIntData(-200, 200, seed);
- float[] floatSrc = intData.ConvertAllToFloat();
-
- ReferenceImplementations.IntegerDCT.TransformFDCTInplace(intData);
-
- float[] dest = new float[64];
- float[] temp = new float[64];
-
- ReferenceImplementations.FastFloatingPointDCT.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));
- }
- }
}
}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegUtilityTestFixture.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegUtilityTestFixture.cs
index 51c99d261a..19602579ab 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegUtilityTestFixture.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegUtilityTestFixture.cs
@@ -76,8 +76,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
return result;
}
- internal static float[] Create8x8RandomFloatData(int minValue, int maxValue, int seed = 42)
- => SpanExtensions.ConvertAllToFloat(Create8x8RandomIntData(minValue, maxValue, seed));
+ internal static float[] Create8x8RoundedRandomFloatData(int minValue, int maxValue, int seed = 42)
+ => Create8x8RandomIntData(minValue, maxValue, seed).ConvertAllToFloat();
+
+ public static float[] Create8x8RandomFloatData(float minValue, float maxValue, int seed = 42)
+ {
+ Random rnd = new Random(seed);
+ float[] result = new float[64];
+ for (int i = 0; i < 8; i++)
+ {
+ for (int j = 0; j < 8; j++)
+ {
+ double val = rnd.NextDouble();
+ val *= maxValue - minValue;
+ val += minValue;
+
+ result[i * 8 + j] = (float)val;
+ }
+ }
+ return result;
+ }
internal void Print8x8Data(T[] data) => this.Print8x8Data(new Span(data));
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
index 3f1cd89b40..5798491ff4 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
@@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
internal float GetBlockValue(Block8x8 block, int x, int y)
{
float d = (this.MaxVal - this.MinVal);
- float val = block.GetValueAt(x, y);
+ float val = block[y, x];
val -= this.MinVal;
val /= d;
return val;
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs
new file mode 100644
index 0000000000..d6c2bc454a
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs
@@ -0,0 +1,121 @@
+namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
+{
+ using System;
+
+ using SixLabors.ImageSharp.Formats.Jpeg.Common;
+
+ internal static partial class ReferenceImplementations
+ {
+ ///
+ /// Reference implementations based on:
+ /// https://github.com/keithw/mympeg2enc/blob/master/idct.c#L222
+ /// Claiming:
+ /// /* reference idct taken from "ieeetest.c"
+ /// * Written by Tom Lane (tgl@cs.cmu.edu).
+ /// * Released to public domain 11/22/93.
+ /// */
+ ///
+ internal static class AccurateDCT
+ {
+ private static double[,] CosLut = InitCosLut();
+
+
+ public static Block8x8 TransformIDCT(ref Block8x8 block)
+ {
+ Block8x8F temp = block.AsFloatBlock();
+ Block8x8F res0 = TransformIDCT(ref temp);
+ return res0.RoundAsInt16Block();
+ }
+
+ public static void TransformIDCTInplace(Span span)
+ {
+ var temp = new Block8x8();
+ temp.LoadFrom(span);
+ Block8x8 result = TransformIDCT(ref temp);
+ result.CopyTo(span);
+ }
+
+ public static Block8x8 TransformFDCT(ref Block8x8 block)
+ {
+ Block8x8F temp = block.AsFloatBlock();
+ Block8x8F res0 = TransformFDCT(ref temp);
+ return res0.RoundAsInt16Block();
+ }
+
+ public static void TransformFDCTInplace(Span span)
+ {
+ var temp = new Block8x8();
+ temp.LoadFrom(span);
+ Block8x8 result = TransformFDCT(ref temp);
+ result.CopyTo(span);
+ }
+
+
+
+ private static double[,] InitCosLut()
+ {
+ double[,] coslu = new double[8,8];
+ int a, b;
+ double tmp;
+
+ for (a = 0; a < 8; a++)
+ for (b = 0; b < 8; b++)
+ {
+ tmp = Math.Cos((double)((a + a + 1) * b) * (3.14159265358979323846 / 16.0));
+ if (b == 0)
+ {
+ tmp /= Math.Sqrt(2.0);
+ }
+ coslu[a, b] = tmp * 0.5;
+ }
+ return coslu;
+ }
+
+ public static Block8x8F TransformIDCT(ref Block8x8F block)
+ {
+ int x, y, u, v;
+ double tmp, tmp2;
+ Block8x8F res = default(Block8x8F);
+
+ for (y=0; y<8; y++) {
+ for (x=0; x<8; x++) {
+ tmp = 0.0;
+ for (v=0; v<8; v++) {
+ tmp2 = 0.0;
+ for (u=0; u<8; u++) {
+ tmp2 += (double) block[v * 8 + u] * CosLut[x, u];
+ }
+ tmp += CosLut[y, v] * tmp2;
+ }
+ res[y, x] = (float)tmp;
+ }
+ }
+ return res;
+ }
+
+
+ public static Block8x8F TransformFDCT(ref Block8x8F block)
+ {
+ int x, y, u, v;
+ double tmp, tmp2;
+ Block8x8F res = default(Block8x8F);
+
+ for (v=0; v<8; v++) {
+ for (u=0; u<8; u++) {
+ tmp = 0.0;
+ for (y=0; y<8; y++) {
+ tmp2 = 0.0;
+ for (x=0; x<8; x++) {
+ tmp2 += (double) block[y, x] * CosLut[x, u];
+ }
+ tmp += CosLut[y, v] * tmp2;
+ }
+ res[v, u] = (float)tmp;
+ }
+ }
+
+ return res;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.IntegerDCT.cs b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs
similarity index 84%
rename from tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.IntegerDCT.cs
rename to tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs
index 1c166748bd..243969cab4 100644
--- a/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.IntegerDCT.cs
+++ b/tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs
@@ -2,12 +2,40 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
{
using System;
+ using SixLabors.ImageSharp.Formats.Jpeg.Common;
+
internal static partial class ReferenceImplementations
{
///
- /// The "original" libjpeg/golang based DCT implementation is used as reference implementation for tests.
+ /// Contains the "original" golang based DCT/IDCT implementations as reference implementations.
+ /// 1. ===== Forward DCT =====
+ /// **** The original golang source claims:
+ /// It is based on the code in jfdctint.c from the Independent JPEG Group,
+ /// found at http://www.ijg.org/files/jpegsrc.v8c.tar.gz.
+ ///
+ /// **** Could be found here as well:
+ /// https://github.com/mozilla/mozjpeg/blob/master/jfdctint.c
+ ///
+ /// 2. ===== Inverse DCT =====
+ ///
+ /// The golang source claims:
+ /// http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_IEC_13818-4_2004_Conformance_Testing/Video/verifier/mpeg2decode_960109.tar.gz
+ /// The referenced MPEG2 code claims:
+ /// /**********************************************************/
+ /// /* inverse two dimensional DCT, Chen-Wang algorithm */
+ /// /* (cf. IEEE ASSP-32, pp. 803-816, Aug. 1984) */
+ /// /* 32-bit integer arithmetic (8 bit coefficients) */
+ /// /* 11 mults, 29 adds per DCT */
+ /// /* sE, 18.8.91 */
+ /// /**********************************************************/
+ /// /* coefficients extended to 12 bit for IEEE1180-1990 */
+ /// /* compliance sE, 2.1.94 */
+ /// /**********************************************************/
+ ///
+ /// **** The code looks pretty similar to the standard libjpeg IDCT, but without quantization:
+ /// https://github.com/mozilla/mozjpeg/blob/master/jidctint.c
///
- public static class IntegerDCT
+ public static class StandardIntegerDCT
{
private const int fix_0_298631336 = 2446;
private const int fix_0_390180644 = 3196;
@@ -37,6 +65,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg.Utils
///
private const int CenterJSample = 128;
+ public static Block8x8 TransformFDCT(ref Block8x8 block)
+ {
+ int[] temp = new int[Block8x8.Size];
+ block.CopyTo(temp);
+ TransformFDCTInplace(temp);
+ var result = default(Block8x8);
+ result.LoadFrom(temp);
+ return result;
+ }
+
+ public static Block8x8 TransformIDCT(ref Block8x8 block)
+ {
+ int[] temp = new int[Block8x8.Size];
+ block.CopyTo(temp);
+ TransformIDCTInplace(temp);
+ var result = default(Block8x8);
+ result.LoadFrom(temp);
+ return result;
+ }
+
///
/// Performs a forward DCT on an 8x8 block of coefficients, including a level shift.
/// Leave results scaled up by an overall factor of 8.