Browse Source

organizing DCT code

af/merge-core
Anton Firszov 9 years ago
parent
commit
66df6fd2eb
  1. 1
      ImageSharp.sln.DotSettings
  2. 26
      src/ImageSharp/Formats/Jpeg/Common/Block8x8.cs
  3. 50
      src/ImageSharp/Formats/Jpeg/Common/Block8x8F.cs
  4. 35
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8FTests.cs
  5. 4
      tests/ImageSharp.Tests/Formats/Jpg/Block8x8Tests.cs
  6. 8
      tests/ImageSharp.Tests/Formats/Jpg/DCTTests.cs
  7. 113
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.FastFloatingPointDCT.cs
  8. 73
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.StandardIntegerDCT.cs
  9. 108
      tests/ImageSharp.Tests/Formats/Jpg/ReferenceImplementationsTests.cs
  10. 22
      tests/ImageSharp.Tests/Formats/Jpg/Utils/JpegUtilityTestFixture.cs
  11. 2
      tests/ImageSharp.Tests/Formats/Jpg/Utils/LibJpegTools.ComponentData.cs
  12. 121
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.AccurateDCT.cs
  13. 52
      tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.StandardIntegerDCT.cs

1
ImageSharp.sln.DotSettings

@ -342,6 +342,7 @@
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/QualifiedUsingAtNestedScope/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AC/@EntryIndexedValue">AC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DC/@EntryIndexedValue">DC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DCT/@EntryIndexedValue">DCT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EOF/@EntryIndexedValue">EOF</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=FDCT/@EntryIndexedValue">FDCT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IDCT/@EntryIndexedValue">IDCT</s:String>

26
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<int> destination)
{
for (int i = 0; i < Size; i++)
{
destination[i] = this[i];
}
}
public void LoadFrom(Span<int> source)
{
for (int i = 0; i < Size; i++)
{
this[i] = (short)source[i];
}
}
[Conditional("DEBUG")]
private static void GuardBlockIndex(int idx)
{

50
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;
}
/// <summary>
/// Pointer-based "Indexer" (getter part)
/// </summary>
@ -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;
//}
/// <summary>
/// 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)
{

35
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);
}
}
}

4
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);
}

8
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<float> sourceArray = JpegUtilityTestFixture.Create8x8RandomFloatData(-200, 200, seed);
Span<float> 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<float> src = JpegUtilityTestFixture.Create8x8RandomFloatData(-200, 200, seed);
Span<float> 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<float> src = JpegUtilityTestFixture.Create8x8RandomFloatData(-200, 200, seed);
Span<float> 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<float> src = JpegUtilityTestFixture.Create8x8RandomFloatData(-200, 200, seed);
Span<float> src = JpegUtilityTestFixture.Create8x8RoundedRandomFloatData(-200, 200, seed);
Block8x8F srcBlock = new Block8x8F();
srcBlock.LoadFrom(src);

113
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<float> 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));
}
}
}
}
}

73
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<int> original = JpegUtilityTestFixture.Create8x8RandomIntData(-200, 200, seed);
Span<int> 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));
}
}
}
}
}

108
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<float> 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<int> original = Create8x8RandomIntData(-200, 200, seed);
Span<int> 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));
}
}
}
}

22
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>(T[] data) => this.Print8x8Data(new Span<T>(data));

2
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;

121
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
{
/// <summary>
/// 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.
/// */
/// </summary>
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<int> 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<int> 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;
}
}
}
}

52
tests/ImageSharp.Tests/Formats/Jpg/Utils/ReferenceImplementations.IntegerDCT.cs → 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
{
/// <summary>
/// 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
/// </summary>
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
/// </summary>
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;
}
/// <summary>
/// Performs a forward DCT on an 8x8 block of coefficients, including a level shift.
/// Leave results scaled up by an overall factor of 8.
Loading…
Cancel
Save