/// Copyright (c) 2008 Jeffrey Powers for Fluxcapacity Open Source. /// Under the MIT License, details: License.txt.. // NOTE: Compile with DYNAMIC_IDCT for a decode performance boost. // May not yield a perceptible boost for small images, // since there is some overhead in emitting CIL dynamically. using System; using System.Reflection.Emit; using System.Reflection; namespace FluxJpeg.Core { /// /// Implements the Discrete Cosine Transform with dynamic CIL /// public partial class DCT { private float[] _temp = new float[64]; // Cosine matrix and transposed cosine matrix private static readonly float[,] c = buildC(); private static readonly float[,] cT = buildCT(); internal DCT() { #if DYNAMIC_IDCT dynamicIDCT = dynamicIDCT ?? EmitIDCT(); #endif } /// /// Precomputes cosine terms in A.3.3 of /// http://www.w3.org/Graphics/JPEG/itu-t81.pdf /// /// Closely follows the term precomputation in the /// Java Advanced Imaging library. /// private static float[,] buildC() { float[,] c = new float[8, 8]; for (int i = 0; i < 8; i++) // i == u or v { for (int j = 0; j < 8; j++) // j == x or y { c[i, j] = i == 0 ? 0.353553391f : /* 1 / SQRT(8) */ (float)(0.5 * Math.Cos(((2.0 * j + 1) * i * Math.PI) / 16.0)); } } return c; } private static float[,] buildCT() { // Transpose i,k <-- j,i float[,] cT = new float[8, 8]; for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) cT[j, i] = c[i, j]; return cT; } public static void SetValueClipped(byte[,] arr, int i, int j, float val) { // Clip into the 0...255 range & round arr[i, j] = val < 0 ? (byte)0 : val > 255 ? (byte)255 : (byte)(val + 0.5); } /// See figure A.3.3 IDCT (informative) on A-5. /// http://www.w3.org/Graphics/JPEG/itu-t81.pdf internal byte[,] FastIDCT(float[] input) { byte[,] output = new byte[8, 8]; #if DYNAMIC_IDCT // Fastest, dynamic MSIL stream dynamicIDCT(input, _temp, output); #else #region Slower, easy-to-read, pure C# IDCT float temp, val = 0; int idx = 0; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { val = 0; for(int k = 0; k < 8; k++) { val += input[i * 8 + k] * c[k, j]; } _temp[idx++] = val; } } for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { temp = 128f; for (int k = 0; k < 8; k++) { temp += cT[i, k] * _temp[k * 8 + j]; } if (temp < 0) output[i, j] = 0; else if (temp > 255) output[i, j] = 255; else output[i, j] = (byte)(temp + 0.5); // Implements rounding } } #endregion #endif return output; } #if DYNAMIC_IDCT /// /// Generates a pure-IL nonbranching stream of instructions /// that perform the inverse DCT. Relies on helper function /// SetValueClipped. /// /// A delegate to the DynamicMethod private static IDCTFunc EmitIDCT() { Type[] args = { typeof(float[]), typeof(float[]), typeof(byte[,]) }; DynamicMethod idctMethod = new DynamicMethod("dynamicIDCT", null, // no return type args); // input arrays ILGenerator il = idctMethod.GetILGenerator(); int idx = 0; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { il.Emit(OpCodes.Ldarg_1); // 1 {temp} il.Emit(OpCodes.Ldc_I4_S, (short)idx++); // 3 {temp, idx} for (int k = 0; k < 8; k++) { il.Emit(OpCodes.Ldarg_0); // {in} il.Emit(OpCodes.Ldc_I4_S, (short)(i * 8 + k)); // {in,idx} il.Emit(OpCodes.Ldelem_R4); // {in[idx]} il.Emit(OpCodes.Ldc_R4, c[k, j]); // {in[idx],c[k,j]} il.Emit(OpCodes.Mul); // {in[idx]*c[k,j]} if (k != 0) il.Emit(OpCodes.Add); } il.Emit(OpCodes.Stelem_R4); // {} } } var meth = typeof(DCT).GetMethod("SetValueClipped", BindingFlags.Static | BindingFlags.Public, null, CallingConventions.Standard, new Type[] { typeof(byte[,]), // arr typeof(int), // i typeof(int), // j typeof(float) } // val , null); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { il.Emit(OpCodes.Ldarg_2); // {output} il.Emit(OpCodes.Ldc_I4_S, (short)i); // {output,i} il.Emit(OpCodes.Ldc_I4_S, (short)j); // X={output,i,j} il.Emit(OpCodes.Ldc_R4, 128.0f); // {X,128.0f} for (int k = 0; k < 8; k++) { il.Emit(OpCodes.Ldarg_1); // {X,temp} il.Emit(OpCodes.Ldc_I4_S, (short)(k * 8 + j)); // {X,temp,idx} il.Emit(OpCodes.Ldelem_R4); // {X,temp[idx]} il.Emit(OpCodes.Ldc_R4, cT[i, k]); // {X,temp[idx],cT[i,k]} il.Emit(OpCodes.Mul); // {X,in[idx]*c[k,j]} il.Emit(OpCodes.Add); } il.EmitCall(OpCodes.Call, meth, null); } } il.Emit(OpCodes.Ret); return (IDCTFunc)idctMethod.CreateDelegate(typeof(IDCTFunc)); } private delegate void IDCTFunc(float[] input, float[] temp, byte[,] output); private static IDCTFunc dynamicIDCT = null; #endif } }