/******************************************************************************/
/*
 * File name: OffsetGain.cpp
 * Location: See Matrox Example Launcher in the MIL Control Center
 * 
 *
 * Synopsis:  This example shows how to use a custom FPGA accelerated
 *            processing function that performs an OffsetGain operation.
 *
 * Note: This example allocates the Offset and Gain buffers in separate
 *       memory banks of the Radient. The Gain buffer is allocated in 
 *       processing SDRAM memory. The Offset buffer is allocated in 
 *       processing SRAM memory. This helps in improving memory efficiency
 *       by spreading bandwidth across multiple memory banks. This example
 *       requires the GainOffsetSxxCx.firmware configuration to work properly.
 *       You can choose this configuration using MilConfig's ProcessingFPGA
 *       section.
 *
 * Copyright (C) Matrox Electronic Systems Ltd., 1992-2020.
 * All Rights Reserved
 */

/* Headers. */
#include <mil.h>

#define BUFFERING_SIZE_MAX 20

/* User's processing function prototype. */
MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr);

/* User's processing function hook data structure. */
typedef struct
   {
   MIL_ID  MilImageOffset;
   MIL_ID  MilImageGain;
   MIL_ID  MilImageDisp;
   MIL_INT DivisionFactor;
   MIL_INT ProcessedImageCount;
   } HookDataStruct;

/* Master MIL functions declarations. */
void FPGAOffsetGain(MIL_ID  SrcImageId, MIL_ID  OffsetImageId,
     MIL_ID GainImageId, MIL_INT Src4, MIL_ID  DstImageId);

/* Main function. */
int MosMain(void)
   {
   MIL_ID   MilApplication;
   MIL_ID   MilSystem     ;
   MIL_ID   MilDisplay    ;
   MIL_ID   MilDigitizer  ;
   MIL_ID   MilGrabBufferList[BUFFERING_SIZE_MAX] = { 0 };
   MIL_INT  MilGrabBufferListSize;
   MIL_ID   MilImageGain  ;
   MIL_ID   MilImageOffset;
   MIL_ID   MilImageDisp  ;
   MIL_INT  SizeX, SizeY, BoardType, NbFrames = 0;
   MIL_INT ProcessFrameCount  = 0;
   MIL_DOUBLE ProcessFrameRate = 0;
   HookDataStruct UserHookData;

   /* Allocate defaults. */
   MappAllocDefault(M_DEFAULT, &MilApplication, &MilSystem, &MilDisplay, &MilDigitizer, M_NULL);

   /* Check if the processing FPGA is present. */
   MsysInquire(MilSystem, M_BOARD_TYPE, &BoardType);
   if(!(BoardType & M_PF))
      {
      MosPrintf(MIL_TEXT("Error, this example needs a processing FPGA installed on your\n"));
      MosPrintf(MIL_TEXT("board to continue.\n"));
      MosPrintf(MIL_TEXT("Exiting example.\n"));
      MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, M_NULL);
      return 0;
      }

   /* Inquire digitizer acquisition size.*/
   SizeX = MdigInquire(MilDigitizer, M_SIZE_X, M_NULL);
   SizeY = MdigInquire(MilDigitizer, M_SIZE_Y, M_NULL);

   /* Allocate Gain and Offset buffers in separate memory banks.
      This will help throughput because bandwidth is now spread across multiple banks.
      M_FAST_MEMORY indicates the buffer will be in SRAM.
      In the event that the board does not have an SRAM memory bank you can simply
      remove the M_FAST_MEMORY attribute below. This will allow the example to
      run on boards without SRAM memory banks. */
   MbufAlloc2d(MilSystem, SizeX, SizeY, 8+M_UNSIGNED, 
               M_IMAGE+M_PROC+M_FPGA_ACCESSIBLE, &MilImageGain);
   MbufClear(MilImageGain, 16);
   MbufAlloc2d(MilSystem, SizeX, SizeY, 8+M_UNSIGNED,
               M_IMAGE+M_PROC+M_FPGA_ACCESSIBLE+M_FAST_MEMORY, &MilImageOffset);
   MbufClear(MilImageOffset, 2);

   /* Allocate a display buffer and initialize it.  For FPGA acceleration, destination 
      buffers can be in host FPGA accessible memory or in FPGA memory.
   */
   MbufAlloc2d(MilSystem, SizeX, SizeY, 8+M_UNSIGNED,
               M_IMAGE+M_GRAB+M_DISP+M_HOST_MEMORY+M_FPGA_ACCESSIBLE, &MilImageDisp);
   MbufClear(MilImageDisp, 0);
   MdispSelect(MilDisplay, MilImageDisp);

   /* Start a continuous grab on the display. */
   MdigGrabContinuous(MilDigitizer, MilImageDisp);

   MosPrintf(MIL_TEXT("This example demonstrates how to use the Mfpga API to program\n"));
   MosPrintf(MIL_TEXT("an OffsetGain primitive and do live processing.\n"));
   MosPrintf(MIL_TEXT("Press <Enter> to continue.\n\n"));
   MosGetchar();

   /* Start the live processing. */
   MdigHalt(MilDigitizer);
   
   /* Allocate buffers and initialize them. For FPGA acceleration, all source buffers
   must be in FPGA accessible memory.
   */
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_DISABLE);
   for (MilGrabBufferListSize = 0;
      MilGrabBufferListSize<BUFFERING_SIZE_MAX; MilGrabBufferListSize++)
      {
      MbufAlloc2d(MilSystem,
         SizeX,
         SizeY,
         8 + M_UNSIGNED,
         M_IMAGE + M_GRAB + M_PROC + M_FPGA_ACCESSIBLE,
         &MilGrabBufferList[MilGrabBufferListSize]);

      if (MilGrabBufferList[MilGrabBufferListSize])
         MbufClear(MilGrabBufferList[MilGrabBufferListSize], 0xFF);
      else
         break;
      }
   MappControl(M_DEFAULT, M_ERROR, M_PRINT_ENABLE);

   /* Initialize the User's processing function data structure. */
   UserHookData.MilImageDisp        = MilImageDisp;
   UserHookData.MilImageOffset      = MilImageOffset;
   UserHookData.MilImageGain        = MilImageGain;
   UserHookData.DivisionFactor      = 8;
   UserHookData.ProcessedImageCount = 0;

   /* Start the processing. The processing function is called for every frame grabbed. */
   MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
                             M_START, M_DEFAULT, ProcessingFunction, &UserHookData);

   /* NOTE: Now the main() is free to perform other tasks while the processing is executing. */
   /* --------------------------------------------------------------------------------- */

   /* Print a message and wait for a key press after a minimum number of frames. */
   MosPrintf(MIL_TEXT("Press <Enter> to stop.\n\n"));
   MosGetch();

   /* Stop the processing. */
   MdigProcess(MilDigitizer, MilGrabBufferList, MilGrabBufferListSize,
               M_STOP, M_DEFAULT, ProcessingFunction, &UserHookData);

   /* Print statistics. */
   MdigInquire(MilDigitizer, M_PROCESS_FRAME_COUNT,  &ProcessFrameCount);
   MdigInquire(MilDigitizer, M_PROCESS_FRAME_RATE,   &ProcessFrameRate);
   MosPrintf(MIL_TEXT("\n\n%lld frames grabbed at %.1f frames/sec (%.1f ms/frame).\n"),
                          (long long)ProcessFrameCount, ProcessFrameRate, 1000.0/ProcessFrameRate);
   MosPrintf(MIL_TEXT("Press <Enter> to end.\n\n"));
   MosGetch();

   /* Free the MIL objects.*/
   while(MilGrabBufferListSize > 0)
      MbufFree(MilGrabBufferList[--MilGrabBufferListSize]);
   MbufFree(MilImageGain);
   MbufFree(MilImageOffset);
   MbufFree(MilImageDisp);
   MappFreeDefault(MilApplication, MilSystem, MilDisplay, MilDigitizer, M_NULL);

   return 0;
   }

   
/* User's call back processing function called every time a grab buffer is modified. */
/* ----------------------------------------------------------------------------------*/

MIL_INT MFTYPE ProcessingFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr)
   {
   HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;
   MIL_ID ModifiedBufferId;

   /* Retrieve the MIL_ID of the grabbed buffer. */
   MdigGetHookInfo(HookId, M_MODIFIED_BUFFER+M_BUFFER_ID, &ModifiedBufferId);

   /* Perform the processing and update the display. */
   FPGAOffsetGain(ModifiedBufferId, UserHookDataPtr->MilImageOffset, 
                        UserHookDataPtr->MilImageGain, 4, UserHookDataPtr->MilImageDisp);

   /* Print and draw the frame count. */
   UserHookDataPtr->ProcessedImageCount++;
   MosPrintf(MIL_TEXT("Processing frame #%lld.\r"), (long long)UserHookDataPtr->ProcessedImageCount);
   
   return 0;
   }