/*****************************************************************************/
/*
* File name: MColorWarp.cpp 
* Location:  ...\Matrox Imaging\MILxxx\Examples\BoardSpecific\McolorWarp\C++
 *             
*
* Synopsis: This program is an example of MIL procesing operations 
*           accelerated on a GPU. A Bayer image is first converted to 
*           a RGB image, before a sharpen operation then a warping 
*           operation using four reference points are applied. 
*           The example loops varying the warp parameters.
*
*  !! Important notes !!
*
*  See Matrox GPU section in the board specific notes of MIL help for important
*  information regarding MIL GPU development.
*
* Copyright (C) Matrox Electronic Systems Ltd., 1992-2015.
* All Rights Reserved
*/

#include <mil.h>
#include <math.h>

/* Target image specifications. */
#define EXAMPLE_IMAGE_PATH       M_IMAGE_PATH MIL_TEXT("GPU/")
#define IMAGE_FILE               EXAMPLE_IMAGE_PATH MIL_TEXT("lena1Kx1K.tif")
#define MAX_ZOOM_FACTOR          1.5
#define MIN_ZOOM_FACTOR          0.1
#define ZOOM_RATE                0.001
#define ROTATION_RATE            0.1
#define CORNER_FACTOR            0.1f

/* Utility functions. */
inline MIL_DOUBLE ComputeZoomFactor(const MIL_DOUBLE &ZoomFactor);
void ComputeWarpParameters(float* WarpParamArray, MIL_INT ImageWidth, MIL_INT ImageHeight);

/* Main function */
/* ------------- */
int MosMain(void)
   {
   MIL_ID MilApplication,        /* Application identifier */
      MilSystem,                 /* System identifier */
      MilDisplay,                /* Display identifier */
      MilBayer,                  /* Original Bayer source image */
      MilSourceColor,            /* Color image to process */
      MilSharpen,                /* Sharpened color image */
      MilProcessedImage,         /* Sharpened and warped image to display */
      MilWarpParamBuffer,        /* Parameters used to generate warp coefficients */
      MilWarpCoeffBuffer;        /* Coefficient array for warping */

   MIL_INT       ImageWidth, ImageHeight;
   MIL_DOUBLE    FramesPerSecond = 0;
   MIL_DOUBLE    Time            = 0;
   MIL_DOUBLE    ZoomFactor      = 1; 
   MIL_DOUBLE    Rotation        = 0;
   MIL_INT       NbLoop          = 0;
   float         WarpParamArray[12] = {0.0};
   MIL_TEXT_CHAR ImageFile[512]     = {0};

   /* Allocate defaults. */
   MappAlloc(M_NULL, M_DEFAULT, &MilApplication);
   MsysAlloc(M_DEFAULT, M_SYSTEM_GPU, M_DEV0, M_DEFAULT, &MilSystem);
   MdispAlloc(MilSystem, M_DEFAULT, MIL_TEXT("M_DEFAULT"), M_DEFAULT, &MilDisplay);

   /* Restore the source image. */
   MosStrcpy(ImageFile, 512, IMAGE_FILE);
   MbufRestore(ImageFile, MilSystem, &MilBayer);
   MbufInquire(MilBayer, M_SIZE_X, &ImageWidth);
   MbufInquire(MilBayer, M_SIZE_Y, &ImageHeight);

   /* Buffer allocations */
   MbufAllocColor(MilSystem, 3, ImageWidth, ImageHeight,              /* Color buffer */
                  8+M_UNSIGNED, M_IMAGE+M_PROC+M_DISP, &MilSourceColor);
   MbufAllocColor(MilSystem, 3, ImageWidth, ImageHeight,              /* Sharpened buffer */
                  8+M_UNSIGNED, M_IMAGE+M_PROC+M_DISP, &MilSharpen);
   MbufAllocColor(MilSystem, 3, ImageWidth, ImageHeight,              /* Result buffer */
                  8+M_UNSIGNED, M_IMAGE+M_PROC+M_DISP, &MilProcessedImage);

   /* Allocate the perspective warp parameters buffers. */
   MbufAlloc2d(MilSystem, 12L, 1L, 32L+M_FLOAT, M_ARRAY, &MilWarpParamBuffer);
   MbufAlloc2d(MilSystem, 3L, 3L, 32L+M_FLOAT, M_ARRAY, &MilWarpCoeffBuffer);

   /* Copy the source image to display */
   MbufCopy(MilBayer, MilProcessedImage);
   MdispSelect(MilDisplay, MilProcessedImage);

   MosPrintf(MIL_TEXT("GPU acceleration example:\n\n"));
   MosPrintf(MIL_TEXT("A raw Bayer image was loaded on the GPU.\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetch();

   MosPrintf(MIL_TEXT("Processing done on each frame:\n"));
   MosPrintf(MIL_TEXT("1-Bayer conversion.\n2-Convolve (3x3)\n3-Warp polynomial.\n\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to stop.\n\n"));

   /* Reset the timer */
   MappTimer(M_DEFAULT, M_TIMER_RESET+M_SYNCHRONOUS, M_NULL);

   /* Begin animation loop */
   while (!MosKbhit())
      {
      /* Convert the bayer buffer */
      MbufBayer(MilBayer, MilSourceColor, M_DEFAULT, M_BAYER_GB);

      /* Sharpen the resulting color image */
      MimConvolve(MilSourceColor, MilSharpen, M_SHARPEN_8);

      /* Warp effect */
      /*-------------*/
      ComputeWarpParameters(WarpParamArray, ImageWidth, ImageHeight);

      /* Put warp parameters values into the coefficient buffer */
      MbufPut1d(MilWarpParamBuffer, 0L, 12L, WarpParamArray);

      /* Generate the warping coefficients */
      MgenWarpParameter(MilWarpParamBuffer, MilWarpCoeffBuffer, M_NULL, 
                        M_WARP_4_CORNER_REVERSE, M_DEFAULT, M_NULL, M_NULL);

      /* Rotate effect */
      /*--------------*/
      /* Generate the warping coefficients for a translate operation */
      MgenWarpParameter(MilWarpCoeffBuffer, MilWarpCoeffBuffer, M_NULL, 
                        M_WARP_POLYNOMIAL, M_TRANSLATE, -(ImageWidth/2.0), -(ImageHeight/2.0));

      /* Generate the warping coefficients for a rotate operation */
      MgenWarpParameter(MilWarpCoeffBuffer, MilWarpCoeffBuffer, M_NULL,
                        M_WARP_POLYNOMIAL, M_ROTATE, Rotation+=ROTATION_RATE, M_NULL);

      /* Generate the warping coefficients for a translate operation */
      MgenWarpParameter(MilWarpCoeffBuffer, MilWarpCoeffBuffer, M_NULL, 
                        M_WARP_POLYNOMIAL, M_TRANSLATE, (ImageWidth/2.0), (ImageHeight/2.0));

      /* Zoom effect */
      /*------------*/
      /* Compute the next zoom factor */
      ZoomFactor += ComputeZoomFactor(ZoomFactor);

      /* Generate the warping coefficients for a zoom operation */
      MgenWarpParameter(MilWarpCoeffBuffer, MilWarpCoeffBuffer, M_NULL,
                        M_WARP_POLYNOMIAL, M_SCALE, ZoomFactor, ZoomFactor);

      /* Generate the warping coefficients for a translate operation. 
         The translate operation is done in order to center the image
       */
      MgenWarpParameter(MilWarpCoeffBuffer, MilWarpCoeffBuffer, M_NULL, M_WARP_POLYNOMIAL,
                        M_TRANSLATE, (ImageWidth/2)-(ImageWidth*(ZoomFactor/2)),
                       (ImageHeight/2)-(ImageHeight*(ZoomFactor/2)));

      /* Warping */
      MimWarp(MilSharpen, MilProcessedImage, MilWarpCoeffBuffer, 
              M_NULL, M_WARP_POLYNOMIAL, M_BILINEAR+M_OVERSCAN_CLEAR);

      /* Calculate and print the number of frames per second processed. */
      MappTimer(M_DEFAULT, M_TIMER_READ+M_SYNCHRONOUS, &Time);
      NbLoop++;
      FramesPerSecond = NbLoop/Time;
      MosPrintf(MIL_TEXT("Processing speed: %.0f Images/Sec.\r"), FramesPerSecond);
      }

   /* Free objects. */
   MbufFree(MilSharpen);
   MbufFree(MilBayer);
   MbufFree(MilSourceColor);
   MbufFree(MilProcessedImage);
   MbufFree(MilWarpCoeffBuffer);
   MbufFree(MilWarpParamBuffer);
   MdispFree(MilDisplay);
   MsysFree(MilSystem);
   MappFree(MilApplication);
   }

/* Generate coefficients to move image corners. */
/* -------------------------------------------- */
void ComputeWarpParameters(float* WarpParamArray, MIL_INT ImageWidth, MIL_INT ImageHeight)
   {
   static MIL_INT PhaseCnt   = 0;
   static bool    FirstTrans = true;

   WarpParamArray[8]  = 0.0;                        /* X of rectangle's top-left corner */
   WarpParamArray[9]  = 0.0;                        /* Y of rectangle's top-left corner */
   WarpParamArray[10] = (float)ImageWidth;          /* X of rectangle's bottom-right corner */
   WarpParamArray[11] = (float)ImageHeight;         /* Y of rectangle's bottom-right corner */
   switch(PhaseCnt)
      {
      /* Phase 0: Reset phase*/
      case 0: 
         /* Fill the Warp parameters array*/
         WarpParamArray[0]  = 0;                    /* X of quadrilateral's 1st corner */
         WarpParamArray[1]  = 0;                    /* Y of quadrilateral's 1st corner */
         WarpParamArray[2]  = (float)ImageWidth ;   /* X of quadrilateral's 2nd corner */
         WarpParamArray[3]  = 0;                    /* Y of quadrilateral's 2nd corner */
         WarpParamArray[4]  = (float)ImageWidth;    /* X of quadrilateral's 3rd corner */
         WarpParamArray[5]  = (float)ImageHeight;   /* Y of quadrilateral's 3rd corner */
         WarpParamArray[6]  = 0;                    /* X of quadrilateral's 4th corner */
         WarpParamArray[7]  = (float)ImageHeight;   /* Y of quadrilateral's 4th corner */
         if (FirstTrans)
            {
            PhaseCnt   = 1;
            FirstTrans = false;
            }
         else
            {
            PhaseCnt   = 3;
            FirstTrans = true;
            }
         break;
      case 1:
         //Phase 1: 1st quarter of the animation
         if ((WarpParamArray[0] >= (ImageWidth)/3))
            {
            PhaseCnt = 2;
            }
         /* Fill the Warp parameters array*/
         WarpParamArray[0]  += CORNER_FACTOR;        /* X of quadrilateral's 1st corner */
         WarpParamArray[1]  += CORNER_FACTOR;        /* Y of quadrilateral's 1st corner */
         WarpParamArray[2]  = (float)ImageWidth;     /* X of quadrilateral's 2nd corner */
         WarpParamArray[3]  = 0;                     /* Y of quadrilateral's 2nd corner */
         WarpParamArray[4]  -= CORNER_FACTOR;        /* X of quadrilateral's 3rd corner */
         WarpParamArray[5]  -= CORNER_FACTOR;        /* Y of quadrilateral's 3rd corner */
         WarpParamArray[6]  = 0;                     /* X of quadrilateral's 4th corner */
         WarpParamArray[7]  = (float)ImageHeight;    /* Y of quadrilateral's 4th corner */
         break; 
      case 2:
         //Phase 2: 2nd quarter of the animation
         if (WarpParamArray[0] <= 0)
            {
            PhaseCnt = 0;
            }
         /* Fill the Warp parameters array*/
         WarpParamArray[0]  -= CORNER_FACTOR;        /* X of quadrilateral's 1st corner */
         WarpParamArray[1]  -= CORNER_FACTOR;        /* Y of quadrilateral's 1st corner */
         WarpParamArray[2]  = (float)ImageWidth;     /* X of quadrilateral's 2nd corner */
         WarpParamArray[3]  = 0;                     /* Y of quadrilateral's 2nd corner */
         WarpParamArray[4]  += CORNER_FACTOR ;       /* X of quadrilateral's 3rd corner */
         WarpParamArray[5]  += CORNER_FACTOR;        /* Y of quadrilateral's 3rd corner */
         WarpParamArray[6]  = 0;                     /* X of quadrilateral's 4th corner */
         WarpParamArray[7]  = (float)ImageHeight;    /* Y of quadrilateral's 4th corner */
         break;
      case 3:
         //Phase 3: 3rd quarter of the animation
         if ((WarpParamArray[2] <= (ImageWidth)-((ImageWidth)/3)))
            {
            PhaseCnt = 4;
            }
         /* Fill the Warp parameters array*/
         WarpParamArray[0]  = 0;                     /* X of quadrilateral's 1st corner */
         WarpParamArray[1]  = 0;                     /* Y of quadrilateral's 1st corner */
         WarpParamArray[2]  -= CORNER_FACTOR;        /* X of quadrilateral's 2nd corner */
         WarpParamArray[3]  += CORNER_FACTOR;        /* Y of quadrilateral's 2nd corner */
         WarpParamArray[4]  = (float)ImageWidth;     /* X of quadrilateral's 3rd corner */
         WarpParamArray[5]  = (float)ImageHeight;    /* Y of quadrilateral's 3rd corner */
         WarpParamArray[6]  += CORNER_FACTOR;        /* X of quadrilateral's 4th corner */
         WarpParamArray[7]  -= CORNER_FACTOR;        /* Y of quadrilateral's 4th corner */
         break; 
      case 4:
         //Phase 4: 4th quarter of the animation
         if (WarpParamArray[2] >= (ImageWidth))
            {
            PhaseCnt = 0;
            }
         /* Fill the Warp parameters array*/
         WarpParamArray[0]  = 0;                     /* X of quadrilateral's 1st corner */
         WarpParamArray[1]  = 0;                     /* Y of quadrilateral's 1st corner */
         WarpParamArray[2]  += CORNER_FACTOR;        /* X of quadrilateral's 2nd corner */
         WarpParamArray[3]  -= CORNER_FACTOR;        /* Y of quadrilateral's 2nd corner */
         WarpParamArray[4]  = (float)ImageWidth;     /* X of quadrilateral's 3rd corner */
         WarpParamArray[5]  = (float)ImageHeight;    /* Y of quadrilateral's 3rd corner */
         WarpParamArray[6]  -= CORNER_FACTOR;        /* X of quadrilateral's 4th corner */
         WarpParamArray[7]  += CORNER_FACTOR;        /* Y of quadrilateral's 4th corner */
         break;
      }
   }

/* Invert zoom rate when reaching zoom limits. */
/* ------------------------------------------- */
inline MIL_DOUBLE ComputeZoomFactor(const MIL_DOUBLE &ZoomFactor)
   {
   static MIL_DOUBLE ZoomRate = ZOOM_RATE;

   if(ZoomFactor <= MIN_ZOOM_FACTOR)
      ZoomRate = ZOOM_RATE;
   else if (ZoomFactor >= MAX_ZOOM_FACTOR)
      ZoomRate = -ZOOM_RATE;

   return ZoomRate;
   }