/********************************************************************/
/** @file Previewer.cpp 
 * Description: Camera image canvas.				    *
 ********************************************************************/
#include <math.h>
#include <stdlib.h>

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
  #include <wx/wx.h>
#endif
#include <wx/cursor.h>
#include <wx/dcbuffer.h>
#include <wx/rawbmp.h>

#include "Previewer.h"
#include "RT_EXEC.h"
#include "../library/hardware/OsSupp.h"
#include "../library/images/ras_comp.h"

#include <strmif.h>

#define DEBUG   0


/**
*  ----------------------------------------------------------------------------
*  event tables and other macros for wxWidgets
*  ----------------------------------------------------------------------------
*
*  the event tables connect the wxWidgets events with the functions (event
*  handlers) which process them. It can be also done at run-time, but for the
*  simple menu events like this the static method is much simpler.
*
*/
BEGIN_EVENT_TABLE(CameraPreview, wxPanel) //ScrolledWindow)
    EVT_MOUSE_EVENTS(       CameraPreview::OnMouseMove)
    EVT_PAINT(              CameraPreview::OnPaint)
    EVT_ERASE_BACKGROUND(   CameraPreview::OnEraseBackground)
    EVT_TIMER(ID_TIMER_PREVIEW, CameraPreview::OnPreviewImageTimer)
END_EVENT_TABLE()


/**
 *  Constructor.
 *  @param[in] parent The parent window object.  */
CameraPreview::CameraPreview(wxWindow *parent, CameraTool *NewCT): 
	      wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 
                 wxRAISED_BORDER|wxTAB_TRAVERSAL|wxNO_FULL_REPAINT_ON_RESIZE|wxCLIP_CHILDREN),
              DEFAULT_BGROUND(_T("BLACK")),
              DEFAULT_TCIRCLE_COLOR(_T("RED")),
              DEFAULT_VCIRCLE_COLOR(_T("YELLOW")),	//WHITE
              DEFAULT_GRID_LINE(_T("WHITE"), 2, wxDOT),
              DIVISOR_RATIO(20),
              ViewMode(RAW_COLOR),
	      filter(0),
              vflip(0),
              hflip(0),
	      rectangle(0),
	      CT(NewCT)
{
  ImageChanged = false;
  bit = NULL;

  ImageBuf = CreateRaster2DRGB(1,1,8);
  ImageBuf->UsageCount++;
  FilterBuf = CreateRaster2DRGB(1,1,8);
  FilterBuf->UsageCount++;

	/* Initialise circles first of all */
  targetCircle = new CircRTShape(&MRT.ReqX,&MRT.ReqY);
  targetCircle->PaintCross=true;
  videoCircle = new CircRTShape(MRT.VideoX,MRT.VideoY);
  if(CT)
    {
    videoCircle->xPos = CT->GetVideoX();
    videoCircle->yPos = CT->GetVideoY();
    }
    
  targetCircle->Tx = videoCircle->Tx=&Tx;
  targetCircle->color = DEFAULT_TCIRCLE_COLOR;
  targetCircle->style = wxTRANSPARENT;
  videoCircle->color = DEFAULT_VCIRCLE_COLOR;
  videoCircle->style = wxTRANSPARENT;	//wxSOLID;
  targetCircle->Radius = videoCircle->Radius = 2.0/DIVISOR_RATIO;

  backgroundColor = wxColour(DEFAULT_BGROUND);

  wxSize size = this->GetSize(); 
  Tx.X.End = -1-videoCircle->Radius/2;
  Tx.X.Start = +1+videoCircle->Radius/2;
  Tx.Y.End = -1-videoCircle->Radius/2;
  Tx.Y.Start = +1+videoCircle->Radius/2;

  Tx.X.CanvasEnd=size.GetX()+size.GetWidth(); //wxSize size;
  Tx.X.CanvasStart=size.GetX(); // + radius/2;
  Tx.Y.CanvasEnd=size.GetY()+size.GetHeight();
  Tx.Y.CanvasStart=size.GetY();

  GrY.Tx=GrX.Tx=&Tx;
  GrX.Ticks=GrY.Ticks=GRID_LINE_NUMBER;
  GrX.LinePen=GrY.LinePen=wxPen(DEFAULT_GRID_LINE);

      // Initializes all available image handlers
  wxInitAllImageHandlers();
  
  wxCursor down_cursor;
  if(WxLoadCursor(&down_cursor,_T("needle.cur")))
    {      
    this->SetCursor(wxCursor(down_cursor));
    }
  else  
    {
#ifdef DEBUG    
        wxString str;
        str.Printf(wxT(_("Error occured camera cursor loading!")));
        wxMessageBox(str, _T("CameraPreviewCameraPreview::constructor()"), wxOK | wxICON_INFORMATION, this);
#endif
    }
    
  m_dragMode = TEST_DRAG_NONE;        
}


CameraPreview::~CameraPreview()
{
  //if(mPreviewTimer.IsRunning) mPreviewTimer->Stop();
  if(targetCircle) {delete targetCircle;targetCircle=NULL;}
  if(videoCircle) {delete videoCircle;videoCircle=NULL;}
  if(ImageBuf)
  {
    if(InterlockedDecrement(&ImageBuf->UsageCount)<=0)
    {
      Raster2DAbstract *tmpR2=ImageBuf;
      ImageBuf=NULL;
      delete tmpR2;
    }
  }
  if(FilterBuf)
  {
    if(InterlockedDecrement(&FilterBuf->UsageCount)<=0)
    {
      Raster2DAbstract *tmpR2=FilterBuf;
      FilterBuf=NULL;
      delete tmpR2;
    }
  }
  if(bit!=NULL) {delete bit;bit=NULL;}
}


void CameraPreview::SetImage(Raster2DAbstract * pRas)
{
  Raster2DAbstract *pRasDest = (filter==0) ? ImageBuf : FilterBuf;
  if(pRasDest && pRasDest!=pRas)
  {
    InterlockedExchangeAdd(&pRasDest->UsageCount,WRITE_BARIER);
    if(pRas == NULL)
    {
      pRasDest->Erase();
    }
    else
    {
      pRasDest->Allocate2D(pRas->Size1D,pRas->Size2D);
      for(unsigned y=0; y<pRas->Size2D; y++)
      {
        pRasDest->Set(y,*pRas->GetRowRaster(y));
      }
    }
    InterlockedExchangeAdd(&pRasDest->UsageCount,-WRITE_BARIER);    
  }
  ImageChanged = true;
}


/** Periodical timer for controll */
void CameraPreview::OnPreviewImageTimer(wxTimerEvent& WXUNUSED(event)) 
{
    // refresh the picture panel
  Refresh();
}


void CameraPreview::OnCameraChange(wxCommandEvent & event)
{
  if(videoCircle)
    {
    videoCircle->xPos = MRT.VideoX;
    videoCircle->yPos = MRT.VideoY;
    }
}


/**
 *  When the size is changed, it is called and then it refreshs the screen and sets the layouts   
 * 
 *  @param[in] event Unused event
 *  \warning If the drawing depends on the size of the window, 
 *  \warning clearing the DC explicitly may be needed and repainting the whole window. 
 *  \warning In which case, calling <CODE>wxWindow::Refresh()</CODE> may be needed to invalidate the entire window. */
void CameraPreview::OnEraseBackground(wxEraseEvent& WXUNUSED(event))
{
   // do nothing
}


/** Set all values to default and repaint camera canvas. */
void CameraPreview::Reset()
{
  MRT.ReqX.SetDouble(0);
  MRT.ReqY.SetDouble(0);  

  Refresh(false);	//repaint a Ball&Plate after reset
}


/*
double GetInterval(void)
    {
      static LARGE_INTEGER pFrequency = {0};
      LARGE_INTEGER CurrentTime;

      if(pFrequency.QuadPart==0)
        if(QueryPerformanceFrequency(&pFrequency)==0)
          pFrequency.QuadPart = 1;

      if(pFrequency.QuadPart <= 1) return GetTickCount();

      QueryPerformanceCounter(&CurrentTime);
      return (1000.0*CurrentTime.QuadPart) / pFrequency.QuadPart;
    }
*/


/// This function reveals 4 hidden offsets of wxPixelData.
uint32_t GetOffsets(wxPixelData<wxBitmap, wxNativePixelFormat>::Iterator p)
{
const char * const pData = (char*)&p.Data();
  const unsigned RedOffs = (char*)&p.Red() - pData;
  const unsigned GreenOffs = (char*)&p.Green() - pData;
  const unsigned BlueOffs = (char*)&p.Blue() - pData;
  p++; 
  const unsigned PixOffs = (char*)&p.Data() - pData;
return RedOffs | (GreenOffs<<8) | (BlueOffs<<16) | (PixOffs<<24);
}


/// Convert Raster2DAbstract to wxBitmap and rescalle it during one operation.
wxBitmap *Raster2Bitmap(Raster2DAbstract *pRas, wxBitmap *pBitmap, unsigned width, unsigned height)
{  
  if(pRas==NULL || pRas->Size1D<=0 || pRas->Size2D<=0)
  {
    if(pBitmap!=NULL) delete pBitmap;
    return NULL;
  }

  if(pBitmap==NULL) pBitmap=new wxBitmap(width,height,24);
  else
  {
    if(pBitmap->GetWidth()!=width || pBitmap->GetHeight()!=height)
    {
      delete pBitmap;
      pBitmap = new wxBitmap(width,height,24);
    }
  }

  typedef wxPixelData<wxBitmap, wxNativePixelFormat> PixelData;
  PixelData data(*pBitmap);
  if(!data)
  {
    if(pBitmap!=NULL) delete pBitmap;
    //double T1=GetInterval();
    wxImage img(pRas->Size1D, pRas->Size2D, (unsigned char *)(pRas->GetRow(0)), true);
    img = img.Rescale(width, height);	// we need to resize the image before.
    pBitmap = new wxBitmap(img);
    //T1 = GetInterval() - T1;
    return pBitmap;
  }

  //double T1=GetInterval();
  PixelData::Iterator rowStart(data);
  rowStart.Offset(data, 0, 0);
  //uint32_t Offsets = GetOffsets(rowStart);

  const uint32_t YScale = (pRas->Size2D*0x10000) / height;
  const uint32_t XScale = (pRas->Size1D*0x10000) / width;  
  for(uint32_t y=0; y<height; ++y)
  {
    PixelData::Iterator p = rowStart;
    switch(pRas->GetPlanes())
    {
      case 24:
        {
          const uint8_t *Raw = (uint8_t*)(pRas->GetRow((y*YScale)>>16));
          if(Raw==NULL) break;
          uint32_t xx = 0;
          uint32_t x = width;
          while(x-- > 0)          
          {      
            p.Red() = *Raw;
            p.Green() = Raw[1];
            p.Blue() = Raw[2];

            xx += XScale;	
	    if(xx > 0xFFFF)
            {
              Raw += 3*(xx >> 16);		// Note, after last cycle pointer points behind area, but it is never dereferenced.
	      xx &= 0xFFFF;
	    }
            ++p;
         }
         break;
       }
      case 8:
       {
         const uint8_t *Raw = (uint8_t*)(pRas->GetRow((y*YScale)>>16));
         if(Raw==NULL) continue;
         uint32_t xx = 0;
         uint32_t x = width;
         while(x-- > 0)
         {      
           p.Red() = p.Green() = p.Blue() = *Raw;

           xx += XScale;	
	   if(xx > 0xFFFF)
           {
             Raw += (xx >> 16);
	     xx &= 0xFFFF;
	   }
           ++p;
         }
         break;
       }
      default:    
       {
         const Raster1DAbstract *pRas1D = pRas->GetRowRaster((y*YScale)>>16);
         if(pRas1D==NULL) break;
         RGBQuad RGB;
         for(uint32_t x=0; x<width; ++x, ++p)
         {
           pRas1D->Get((x*XScale)>>16,&RGB);
           p.Red() = RGB.R;
           p.Green() = RGB.G;
           p.Blue() = RGB.B;
         } 
         break;
       }
    }
    rowStart.OffsetY(data, 1);
  }
  //T1 = GetInterval() - T1;
  return pBitmap;
}


/**  Paints the graphs. 
 *  @param[in] event Unused event */
void CameraPreview::OnPaint(wxPaintEvent &WXUNUSED(event))
{   
int width;
int height;

  GetClientSize(&width, &height);

  Tx.X.CanvasStart = width - SHIFT_PLATE_POS;
  Tx.X.CanvasEnd = 0 + SHIFT_PLATE_POS;  
  Tx.Y.CanvasStart = 0 + SHIFT_PLATE_POS;
  Tx.Y.CanvasEnd = height - SHIFT_PLATE_POS;

  if(bit!=NULL && !ImageChanged)
  {
    if(bit->GetWidth()!=width || bit->GetHeight()!=height) ImageChanged=true;
  }

  if(ImageChanged)
  {
    Raster2DAbstract *pRas = (filter==0) ? ImageBuf : FilterBuf;
    if(pRas!=NULL)
    {
      if(InterlockedIncrement(&pRas->UsageCount) < WRITE_BARIER)		// Image is not reallocating.
      {
        bit = Raster2Bitmap(pRas, bit, width, height);		// Convert raster to wxBitmap object
        ImageChanged = false;
      }
      InterlockedDecrement(&pRas->UsageCount);
    }
  }

  if(ViewMode>GRAPH && bit!=NULL)
  {
    wxPaintDC dc(this);		//do not use buffers, because we have a bitmap only
    dc.DrawBitmap(*bit, 0, 0);
    videoCircle->Paint(dc);     
  }
  else
  {        
    wxBufferedPaintDC dc(this);

    dc.SetBackground(wxColour("BLACK"));
    /*if(!MemoryIsInitialised)*/ dc.Clear();    

//    DrawGraphLines(dc);
    GrX.Paint(dc);  
    GrY.Paint(dc);  
//    targetCircle->Paint(dc);
    videoCircle->Paint(dc);
  }
}


/**
 *  Handles the mouse events and draws the desired target position.
 *  @param[in] event Used event */
void CameraPreview::OnMouseMove(wxMouseEvent& event)
{
/*
    if (event.LeftDown())
    {   // We tentatively start dragging, but wait for
        // mouse movement before dragging properly.
        m_dragMode = TEST_DRAG_START;
        dragStartPos = event.GetPosition();
    }
    else if (event.LeftUp() && m_dragMode != TEST_DRAG_NONE)
    {   // Finish dragging
        m_dragMode = TEST_DRAG_NONE;

        wxClientDC dcClient(this);
        wxSize size = dcClient.GetSize(); 
        wxBufferedDC dc;  
        dc.Init(&dcClient, size);
        dragStartPos = event.GetPosition();
                
        double val;
        val=Tx.X.Canvas2Log(dragStartPos.x);
	if(val<-1) val=-1;
        if(val>1) val=1;
	MRT.ReqX.SetDouble(val);
	val=Tx.Y.Canvas2Log(dragStartPos.y);
	if(val<-1) val=-1;
        if(val>1) val=1;
        MRT.ReqY.SetDouble(val);
        
        DrawGraphLines(dc);
        GrX.Paint(dc);
        GrY.Paint(dc);        
        targetCircle->Paint(dc);
        videoCircle->Paint(dc);
    }

    else if (event.Dragging() && m_dragMode != TEST_DRAG_NONE)
    {
        if (m_dragMode == TEST_DRAG_START)
        {
            // We will start dragging if we've moved beyond a couple of pixels
            wxPoint pos = event.GetPosition();
            int tolerance = 2;
            int dx = abs(pos.x - dragStartPos.x);
            int dy = abs(pos.y - dragStartPos.y);
            if (dx <= tolerance && dy <= tolerance)
                return;
            
            // Start the drag.
//          m_dragMode = TEST_DRAG_DRAGGING;
                        
            wxClientDC dcClient(this);
            wxSize size = dcClient.GetSize(); 
            wxBufferedDC dc;  
            dc.Init(&dcClient, size);
                       
            wxCoord w, h;
            dc.GetSize(&w, &h);
            wxPoint p = wxPoint(w, h);
            
	    double val;
            val=Tx.X.Canvas2Log(pos.x);
	    if(val<-1) val=-1;
            if(val>1) val=1;
	    MRT.ReqX.SetDouble(val);
	    val=Tx.Y.Canvas2Log(pos.y);
	    if(val<-1) val=-1;
            if(val>1) val=1;
            MRT.ReqY.SetDouble(val);
                        
            DrawGraphLines(dc);            
            GrX.Paint(dc);  
            GrY.Paint(dc);  

	    targetCircle->Paint(dc);
	    videoCircle->Paint(dc);
        }
       // else if (m_dragMode == TEST_DRAG_DRAGGING)
       // {
            // We're currently dragging. See if we're over another shape.
       // }
    }    
*/
}


/** Sets background appearance.
 *
 *  @see wxColour is the reference class for any further question about the parameter of the method.
 *  @param[in] color wxColour value */
void CameraPreview::SetBackground(const wxColour &color)
{   
    if(color.Ok())
        backgroundColor = color;    
    else     
        backgroundColor = wxColor(DEFAULT_BGROUND); 
}


/**
 *  Sets the background of the panel. Unless it is assigned with
 *  different value, it uses the default value, i.e., "RED"
 *
 *  @param[in] devContext A device context is onto which graphics and text can be drawn */
void CameraPreview::SetBackGColorProp(wxDC &devContent)
{  
    // the color of frame line of outter rectangle
  devContent.SetPen(wxPen(wxT("BLUE"), 3, wxSOLID));
  devContent.SetBrush(wxBrush(backgroundColor));
}


/**
 *  Sets the target circle appearance.
 *
 *  @see wxColour is the reference class for any further question about the parameter of the method.
 *  @param[in] color wxColour value  */
void CameraPreview::SetTCircleColor(const wxColour &color)
{   
  if(color.Ok())
     targetCircle->color = color;    
  else     
     targetCircle->color = wxColor(DEFAULT_TCIRCLE_COLOR); 
}


/*
void CameraPreview::AssignImage(Raster2DAbstract *newImage)
{
  if(ImageBuf==newImage) return;
  if(ImageBuf)
  {
    if(ImageBuf->UsageCount-- <= 1)
    {
      delete ImageBuf;
    }
    ImageBuf = NULL;
  }
  if(newImage==NULL) return;
  newImage->UsageCount++;
  ImageBuf = newImage;
}
*/



/** Sets the video circle appearance.
 *  @see wxColour is the reference class for any further question about the parameter of the method.
 *  @param[in] color wxColour value */
void CameraPreview::SetVCircleColor(const wxColour &color)
{   
  if(color.Ok())
    videoCircle->color = color;    
  else     
    videoCircle->color = wxColor(DEFAULT_VCIRCLE_COLOR); 
}


/**
 *  Sets grid line appearence.
 *
 *  @see wxPen is the reference class  for any further question about the parameter of the method. 
 *  @param[in] pen wxPen */
void CameraPreview::SetGridLine(const wxPen &pen)
{
  if(pen.Ok())
    GrX.LinePen=GrY.LinePen = pen;
  else
    GrX.LinePen=GrY.LinePen = wxPen(DEFAULT_GRID_LINE); 
}


/** Captures image according to a ViewMode set. */
bool CameraPreview::CaptureImage(bool DeleteIMSample)
{
  if(CT->Camera==NULL) return false;

  if(ImageBuf==NULL || ImageBuf->UsageCount>=WRITE_BARIER)
  {        //nothing to capture	 
    if(DeleteIMSample) CT->Camera->ClearImediaSample();
    return false;  /// \todo: here could be assert, but I prefer to silently return
  }

  int SampleSize = 0;
  IMediaSample *tmpMediaSample = NULL;
  const unsigned char *SampleData = (const unsigned char *)CT->Camera->PeekImediaSample(&SampleSize,&tmpMediaSample);
  if(tmpMediaSample == NULL) return false;
  if(SampleSize<=0 || SampleData==NULL ||
     SampleSize < (labs(CT->Camera->GetWidth())*labs(CT->Camera->GetHeight())*CT->Camera->GetDepth())/8) // Check for insufficient buffer size.
  {
    tmpMediaSample->Release();
    if(DeleteIMSample) CT->Camera->ClearImediaSample();
    return false;
  }

  ImageBuf->UsageCount++;

  switch(ViewMode)
  {
    case RAW_PIC:         if(ImageBuf->Channels()!=1 && ImageBuf->UsageCount==2)
			  {
                            Raster2DAbstract *pIB = ImageBuf;
			    ImageBuf = NULL;
			    delete pIB;
			    ImageBuf = CreateRaster2D(0, 0, 8);
		            ImageBuf->UsageCount = 2;
			  }
			  CT->ReadImageAs(ImageBuf, ((ImageBuf->Channels()==1)?0x11:0x1)|(rectangle?0x80:0), vflip, hflip, SampleData); 
			  break;

    case THRESHOLDED_PIC: if(ImageBuf->Channels()!=1 && ImageBuf->UsageCount==2)
			  {
                            Raster2DAbstract *pIB = ImageBuf;
			    ImageBuf = NULL;
			    delete pIB;
			    ImageBuf = CreateRaster2D(0, 0, 8);
		            ImageBuf->UsageCount = 2;
		          }
                          CT->ReadImageAs(ImageBuf, ((ImageBuf->Channels()==1)?0x12:0x2)|(rectangle?0x80:0), vflip, hflip, SampleData);
                          break;

    case RAW_COLOR:	  if(ImageBuf->Channels()!=3 && ImageBuf->UsageCount==2)
			  {
                            Raster2DAbstract *pIB = ImageBuf;
			    ImageBuf = NULL;
			    delete pIB;
			    ImageBuf = CreateRaster2DRGB(0, 0, 8);
		            ImageBuf->UsageCount = 2;
		          }
			  CT->ReadImageAs(ImageBuf, 0|(rectangle?0x80:0), vflip, hflip, SampleData);
			  break;

    default:		  goto FAIL_RETURN;		//no capture needed
  }

  SampleData = NULL;
  tmpMediaSample->Release();
  tmpMediaSample = NULL;
  if(DeleteIMSample)
  {
    CT->Camera->ClearImediaSample();
    DeleteIMSample = false;		// Cached media sample already erased.
  }
  
  const int width = ImageBuf->Size1D;
  const int height = ImageBuf->Size2D;
  if(width==0 || height==0) goto FAIL_RETURN;

  if(FilterBuf==NULL) goto FAIL_RETURN;

  if(FilterBuf->UsageCount==1)		// Less than 1 means shutdown sequence, more than 1 means that another function uses it.
  {  
    FilterBuf->UsageCount++;
    if(filter==0)
    {
      InterlockedExchangeAdd(&FilterBuf->UsageCount,WRITE_BARIER);
      FilterBuf->Erase();
      InterlockedExchangeAdd(&FilterBuf->UsageCount,-WRITE_BARIER);
    }
    else
    {
      if(FilterBuf->Channels() != ImageBuf->Channels())
      {
        Raster2DAbstract *pFB = FilterBuf;
        FilterBuf = NULL;
        delete pFB;
        if(ImageBuf->Channels() == 3)
          FilterBuf = CreateRaster2DRGB(width, height, ImageBuf->GetPlanes()/ImageBuf->Channels());
        else
          FilterBuf = CreateRaster2D(width, height, ImageBuf->GetPlanes());
        if(FilterBuf==NULL) goto FAIL_RETURN;
        FilterBuf->UsageCount += 2;
      }

      bool empty = FilterBuf->Data2D==NULL;
      if(width!=FilterBuf->Size1D || height!=FilterBuf->Size2D)
      {
        empty = true;
        InterlockedExchangeAdd(&FilterBuf->UsageCount,WRITE_BARIER);
        FilterBuf->Allocate2D(width,height);
        InterlockedExchangeAdd(&FilterBuf->UsageCount,-WRITE_BARIER);
      }

      InterlockedExchangeAdd(&FilterBuf->UsageCount,READ_BARIER);
      InterlockedExchangeAdd(&ImageBuf->UsageCount,READ_BARIER);
      const long MaxBytes = width * FilterBuf->Channels();
      for(unsigned y=0; y<height; y++)
      {
        unsigned char *ImgData    = (unsigned char *)ImageBuf->GetRow(y);
        unsigned char *FilterData = (unsigned char *)FilterBuf->GetRow(y);
      
        if(empty)
          memcpy(FilterData,ImgData,MaxBytes);
        else
        {
          for(long i=MaxBytes; i>0; i--)
          {
            *FilterData = ((int)*ImgData + (int)*FilterData)/2;
            FilterData++;
	    ImgData++;
          }
        }
      }
      InterlockedExchangeAdd(&ImageBuf->UsageCount,-READ_BARIER);
      InterlockedExchangeAdd(&FilterBuf->UsageCount,-READ_BARIER);      
    }
    if(InterlockedDecrement(&FilterBuf->UsageCount) <= 0)
    {
      delete FilterBuf;
      FilterBuf = NULL;
    }
  }

  if(InterlockedDecrement(&ImageBuf->UsageCount) <= 0)
  {
    delete ImageBuf;
    ImageBuf = NULL;
  }
  ImageChanged = true;
  return true;

FAIL_RETURN:
  if(InterlockedDecrement(&ImageBuf->UsageCount) <= 0)
  {
    delete ImageBuf;
    ImageBuf = NULL;
  }
  if(DeleteIMSample) CT->Camera->ClearImediaSample();
  return false;
}


void CameraPreview::HookFrame(wxFrame *pFrame2Hook)
{
  if(CT->Camera==NULL) return;
  CT->Camera->HookFrame(pFrame2Hook);
}


int CameraPreview::getFrameCounter(void)
{
  if(CT==NULL || CT->Camera==NULL) return 0; 
  const int i = CT->Camera->FrameCounter; 
  CT->Camera->FrameCounter = 0; 
return i;
}