
#include <math.h>
#include <stdio.h>
#include <string.h>

#include "CamTile.h"

// 'MAX' = 1
#define MAX_COEFF 256
#define DBL_COEFF (2 * MAX_COEFF)
#define DIV_SQ2(a) ((a * 724) / 1024)
#define F_SQ05 0.707106781

#define NORM_DC	8
#define NORM_AC	8

#define OFF1	0
#define OFF2	128

#define DCT_INT   0 
#define DCT_FLOAT 1

#define INTF 0
#define INTI 0

static float ff[32] =
{
    1.000000,  0.980785,  0.923880,  0.831470,  0.707107,  0.555570,  0.382683,  0.195090,
    0.000000, -0.195090, -0.382683, -0.555570, -0.707107, -0.831470, -0.923880, -0.980785,
   -1.000000, -0.980785, -0.923879, -0.831469, -0.707106, -0.555570, -0.382683, -0.195089,
    0.000001,  0.195091,  0.382684,  0.555571,  0.707108,  0.831470,  0.923880,  0.980786,
};

static int fi[32] =
{
   255,  250,  236,  212,  180,  142,   98,   50, 
     0,  -50,  -98, -142, -180, -212, -236, -250,
  -255, -250, -236, -212, -180, -142,  -98,  -50,
     0,   50,   98,  142,  180,  212,  236,  250,
};

// 16 bit values
// 32767 32137 30273 27245 23170 18204 12539 6393 0 -6393 -12539 -18204 -23170 -27245 -30273 -32137 -32767 -32137 -30273 -27245 -23170 -18204 -12539 -6392 0 6393 12539 18204 23170 27245 30273 32137

   


/**
  \fn void CCamTile::CalculateForward(uchar *dst, uchar *src)
  \brief Perform forward DCT calculation
  \param dst Destination buffer; uses out_offsets
  \param src Source buffer; used in_offsets
 */

void CCamTile::CalculateForward(uchar *dst, uchar *src)
{
   /* This is the 'decomposed' version, first doing rows, then columns.
    * This routine is of O(n^3).
    */
#if INTF
  #define FTYPE int
  #define FT fi   
#else
  #define FTYPE float
  #define FT ff   
#endif

   int i, j, u, v;
   FTYPE g[8][8], dct[8][8], t;

   /* Copy to DCT buffer */
   for (i = 0; i < 8; i++)
      for (j = 0; j < 8; j++)
         dct[i][j] = *(src + in_offsets[i][j]) - OFF1;

   /* horizontal scan */
   for (i = 0; i < 8; i++) {
      for (v = 0; v < 8; v++) {
         t = 0;
         for (j = 0; j < 8; j++)
            t += FT[((j + j + 1) * v) & 31] * dct[i][j];
#if INTF         
         if (v == 0) t = DIV_SQ2(t);
         g[i][v] = t / DBL_COEFF;
#else
         if (v == 0) t *= F_SQ05;
         g[i][v] = t / 2.0;
#endif         
      }
   }
  
   /* vertical scan */
   for (u = 0; u < 8; u++) {
      for (v = 0; v < 8; v++) {
         t = 0;
         for (i = 0; i < 8; i++)
            t += FT[((i + i + 1) * u) & 31] * g[i][v];
#if INTF            
         if (u == 0) t = DIV_SQ2(t);
         dct[u][v] = t / DBL_COEFF;
#else
         if (u == 0) t *= F_SQ05;
         dct[u][v] = t / 2.0;
#endif         
      }
   }

   /* Pfew. Done. Put in destination. */
   for (j = 0; j < 8; j++) {
      for (i = 0; i < 8; i++) {
         t = dct[j][i];
         if (i == 0 && j == 0)
           t /= NORM_DC; // "normalize"
         else
           t = OFF2 + (t / NORM_AC);
#if ! INTF
         t = rint(t); 
#endif
         if (t < 0)   t = 0;
         if (t > 255) t = 255;
         *(dst + out_offsets[j][i]) = t;
      }
   }
}

/**
  \fn void CCamTile::CalculateInverse(uchar *dst, uchar *src)
  \brief Perform inverse DCT calculation
  \param dst Destination buffer; uses in_offsets
  \param src Source buffer; used out_offsets
  
  Performs reverse DCT operation; note that the in/out_offsets 
  are also swapped.
 */
void CCamTile::CalculateInverse(uchar *dst, uchar *src)
{
   /* decomposed version. See forward DCT for comments */
#if INTI
  #define FTYPE int
  #define FT fi   
#else
  #define FTYPE float
  #define FT ff   
#endif

   int i, j, u, v;
   FTYPE g[8][8], dct[8][8], s, t;

   /* Copy to DCT buffer */
   for (i = 0; i < 8; i++)
      for (j = 0; j < 8; j++) {
         t = *(src + out_offsets[i][j]);
         if (i == 0 && j == 0) 
           t *= NORM_DC;
         else
           t = (t - OFF2) * NORM_AC;
         dct[i][j] = t;
      }

   for (u = 0; u < 8; u++) {
      for (j = 0; j < 8; j++) {
         t = 0;
         for (v = 0; v < 8; v++) {
            s = FT[((j + j + 1) * v) & 31] * dct[u][v];
#if INTI
            if (v == 0) s = DIV_SQ2(s);
#else 
            if (v == 0) s *= F_SQ05;
#endif
            t += s;
         }
#if INTI
         g[u][j] = t / DBL_COEFF;
#else
         g[u][j] = t / 2.0;
#endif
      }
   }

   for (i = 0; i < 8; i++) {
      for (j = 0; j < 8; j++) {
         t = 0;
         for (u = 0; u < 8; u++) {
            s = FT[((i + i + 1) * u) & 31] * g[u][j];
#if INTI       
            if (u == 0) s = DIV_SQ2(s);
#else
            if (u == 0) s *= F_SQ05;
#endif            
            t += s;
         }
#if INTI         
         dct[i][j] = t / DBL_COEFF;
#else
         dct[i][j] = rint(t / 2.0);
#endif         
      }
   }

   // Done
   for (j = 0; j < 8; j++) {
      for (i = 0; i < 8; i++) {
         t = OFF1 + dct[i][j];
         if (t < 0)   t = 0;
         if (t > 255) t = 255;
         *(dst + in_offsets[i][j]) = t;
      }
   }
}


/** 
  \fn void CCamTile::CalcDiffValue(const uchar *src)
  \brief Calculate absolute value of difference
  
  Assuming 'src' refers to a Delta panel, calculate the sum of 
  the absolute difference.
*/
void CCamTile::CalcDiffValue(const uchar *src)
{
   int i, j;
   int t;
   
//   abs_value = 0; // Our own contribution
//   nb_value = 0;  // The contribution of our neighbours
   for (i = 0; i < 8; i++) {
      for (j = 0; j < 8; j++) {
         t = *(src + in_offsets[i][j]);
         if (t >= 128)  
           abs_value += (t - 128);
         else
           abs_value += (128 - t);
      }
   }
}


void CCamTile::CopyTile(uchar *dst, const uchar *src)
{
   int i;
   
   if (dst == NULL || src == NULL)
     return;
   for (i = 0; i < 8; i++) 
      memcpy(dst + in_offsets[i][0], src + in_offsets[i][0], 8);
}

/** 
  \fn void CCamTile::ClearInTile(uchar *dst)
  \brief Clear tile
  
  This sets the 64 pixels of the input tile to a value of 128.
   
*/
void CCamTile::ClearInTile(uchar *dst)
{
   int i, j;

   if (dst == NULL)
     return;
   for (i = 0; i < 8; i++)
      for (j = 0; j < 8; j++)
      dst[in_offsets[i][j]] =  128;
}

/** 
  \fn void CCamTile::ClearOutTile(uchar *dst)
  \brief Clear tile
  
  This sets the 64 pixels of the output tile to a value of 128.
   
*/
void CCamTile::ClearOutTile(uchar *dst)
{
   int i, j;

   if (dst == NULL)
     return;
   for (i = 0; i < 8; i++)
      for (j = 0; j < 8; j++)
      dst[out_offsets[i][j]] =  128;
}



// static

int CCamTile::CompareTile(const void *t1, const void *t2)
{
   CCamTile *c1, *c2;
   
   c1 = *(CCamTile **)t2;
   c2 = *(CCamTile **)t1;
   return (c1->abs_value + c1->nb_value) - (c2->abs_value + c2->nb_value);
}
