#include "HistogramCanvas.h"
#include "PropViewFrame.h"
#include <apps/Common/wxAbstraction.h>
#include <math.h>
#include <wx/dcbuffer.h>
#include <wx/grid.h>
#include <wx/settings.h>

using namespace mvIMPACT::acquire;

BEGIN_EVENT_TABLE(HistogramCanvas, ImageAnalysisPlotCanvas)
  EVT_PAINT(HistogramCanvas::OnPaint)
END_EVENT_TABLE()

//-----------------------------------------------------------------------------
HistogramCanvas::HistogramCanvas( wxWindow* parent, const wxString& configName, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */,
	const wxSize& size /* = wxDefaultSize */, long style /* = wxSUNKEN_BORDER */, const wxString& name /* = wxT("histogram") */, bool boActive /* = false */, bool boHandleStandardDeviation /* = false */ ) :
	ImageAnalysisPlotCanvas(parent, configName, id, pos, size, style, name, boActive, 1),
	m_DrawStepWidth(0), m_ppHistogramBuffer(0), m_pHistogramMax(0), m_pHistogramAverage(0), m_pStandardDeviation(0), 
	m_boHandleStandardDeviation(boHandleStandardDeviation), m_CurrentMax(1, 1) {}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
HistogramCanvas::~HistogramCanvas()
//-----------------------------------------------------------------------------
{
	DeallocateHistogramBuffer();
}

//-----------------------------------------------------------------------------
void HistogramCanvas::CalculateMaxValue( void )
//-----------------------------------------------------------------------------
{
	// evaluate new max value
	m_CurrentMax = PlotPoint(m_ppHistogramBuffer[0][0], 0);
	for( int j=0; j<m_ChannelCount; j++ )
	{
		m_pHistogramMax[j] = PlotPoint(m_ppHistogramBuffer[j][0], 0);
		for( unsigned int i=1; i<m_valCount; i++ )
		{
			if( m_ppHistogramBuffer[j][i] > m_pHistogramMax[j].cnt_ )
			{
				m_pHistogramMax[j] = PlotPoint(m_ppHistogramBuffer[j][i], i);
			}
		}
		const double PIX_CNT = static_cast<double>(m_AOIw * m_AOIh);
		m_pHistogramAverage[j] = m_pHistogramAverage[j] / PIX_CNT;
		if( m_pHistogramMax[j].cnt_ == 0 )
		{
			m_pHistogramMax[j].cnt_ = 1;
		}
		if( m_CurrentMax.cnt_ < m_pHistogramMax[j].cnt_ )
		{
			m_CurrentMax = m_pHistogramMax[j];
		}
		if( m_boHandleStandardDeviation )
		{
			double sumOfSquares = 0;
			for( unsigned int i=0; i<m_valCount; i++ )
			{
				if( m_ppHistogramBuffer[j][i] > 0 )
				{
					sumOfSquares += pow(static_cast<double>(i), 2) * m_ppHistogramBuffer[j][i];
				}
			}
			m_pStandardDeviation[j] = sqrt( sumOfSquares / PIX_CNT );
		}
	}
}

//-----------------------------------------------------------------------------
void HistogramCanvas::DeallocateHistogramBuffer( void )
//-----------------------------------------------------------------------------
{
	if( m_ppHistogramBuffer )
	{
		for( int channel=0; channel<m_ChannelCount; channel++ )
		{
			delete [] m_ppHistogramBuffer[channel];
		}
		delete [] m_ppHistogramBuffer;
		m_ppHistogramBuffer = 0;
	}
	delete [] m_pHistogramAverage;
	m_pHistogramAverage = 0;
	delete [] m_pStandardDeviation;
	m_pStandardDeviation = 0;
	delete [] m_pHistogramMax;
	m_pHistogramMax = 0;
}

//-----------------------------------------------------------------------------
void HistogramCanvas::DrawHistogramLine( wxPaintDC& dc, int h, double scaleX, double scaleY, TPlane plane )
//-----------------------------------------------------------------------------
{
	if( !m_ppHistogramBuffer )
	{
		return;
	}

	int lowerStart = h - GetBorderWidth();
	dc.SetPen( *m_Pens[plane].pColour_ );
	unsigned int stepWidth = m_DrawStepWidth;
	if( stepWidth == 0 )
	{
		// '0' is auto mode, which results in no more then 256 different values will be displayed no matter which pixel format is used.
		stepWidth = static_cast<unsigned int>( static_cast<double>((m_valCount / 256)) * (static_cast<double>(GetPercentageToDraw()) / 100.));
	}
	if( stepWidth < 1 )
	{
		stepWidth = 1;
	}
	unsigned int from, to;
	GetDrawRange( &from, &to );
	for( unsigned int i=from; ( i<to-1 ) && ( i+stepWidth<m_valCount ); i+=stepWidth )
	{
		dc.DrawLine( static_cast<int>( GetBorderWidth() + ( ( i - from ) * scaleX ) + 1 ),
					 static_cast<int>( lowerStart - ( m_ppHistogramBuffer[plane][i] * scaleY ) ),
					 static_cast<int>( GetBorderWidth() + ( ( i - from + stepWidth ) * scaleX ) + 1 ),
					 static_cast<int>( lowerStart - ( m_ppHistogramBuffer[plane][i+stepWidth] * scaleY ) ) );
	}
	dc.SetPen( wxNullPen );
}

//-----------------------------------------------------------------------------
wxString HistogramCanvas::GetGridValue( int row, int col ) const
//-----------------------------------------------------------------------------
{
	if( m_ppHistogramBuffer && ( col >= 0 ) && ( row >= 0 ) && ( col < m_ChannelCount ) && ( static_cast<unsigned int>(row) <= m_valCount ) )
	{
		return ( row == 0 ) ? m_Pens[col].description_ : wxString::Format( wxT("%d"), m_ppHistogramBuffer[col][row-1] );
	}
	return wxEmptyString;
}

//-----------------------------------------------------------------------------
void HistogramCanvas::PrepareHistogramBuffer( bool boFormatChanged, int channelCount, mvIMPACT::acquire::TImageBufferPixelFormat pixelFormat )
//-----------------------------------------------------------------------------
{
	if( boFormatChanged || !m_ppHistogramBuffer || !m_pHistogramAverage || !m_pStandardDeviation || !m_pHistogramMax )
	{
		DeallocateHistogramBuffer();
		m_ChannelCount = channelCount;
		m_ppHistogramBuffer = new int*[channelCount];
		m_pHistogramAverage = new double[channelCount];
		m_pStandardDeviation = new double[channelCount];
		m_pHistogramMax = new PlotPoint[channelCount];
		assert( ( channelCount <= PLANE_CNT ) && wxT("Somebody has defined more channels then this tool can handle") );
		for( int channel=0; channel<channelCount; channel++ )
		{
			switch( pixelFormat )
			{
			case ibpfMono10:
			case ibpfRGB101010Packed:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_10_BIT];
				m_valCount = VAL_COUNT_10_BIT;
				break;
			case ibpfMono12:
			case ibpfRGB121212Packed:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_12_BIT];
				m_valCount = VAL_COUNT_12_BIT;
				break;
			case ibpfMono14:
			case ibpfRGB141414Packed:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_14_BIT];
				m_valCount = VAL_COUNT_14_BIT;
				break;
			case ibpfMono16:
			case ibpfRGB161616Packed:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_16_BIT];
				m_valCount = VAL_COUNT_16_BIT;
				break;
			default:
				m_ppHistogramBuffer[channel] = new int[VAL_COUNT_8_BIT];
				m_valCount = VAL_COUNT_8_BIT;
				break;
			}
		}

		if( m_pNumericalDisplay )
		{
			int columnCount = m_pNumericalDisplay->GetTable()->GetNumberCols();
			if( columnCount < channelCount )
			{
				m_pNumericalDisplay->AppendCols( channelCount - columnCount );
			}
			else if( columnCount > channelCount )
			{
				m_pNumericalDisplay->DeleteCols( columnCount - 1, columnCount - channelCount );
			}
			for( int i=0; i<channelCount; i++ )
			{
				m_pNumericalDisplay->SetCellBackgroundColour( 0, i, *m_Pens[i].pColour_ );
				m_pNumericalDisplay->SetCellTextColour( 0, i, *wxWHITE );
				m_pNumericalDisplay->SetCellValue( 0, i, m_Pens[i].description_ );
			}

			unsigned int rowCount = static_cast<unsigned int>(m_pNumericalDisplay->GetTable()->GetNumberRows());
			if( rowCount < ( m_valCount + 1 ) )
			{
				m_pNumericalDisplay->AppendRows( m_valCount + 1 - rowCount );
			}
			else if( rowCount > ( m_valCount + 1 ) )
			{
				m_pNumericalDisplay->DeleteRows( rowCount - 1, rowCount - m_valCount + 1 );
			}
		}
	}

	for( int channel=0; channel<channelCount; channel++ )
	{
		memset( m_ppHistogramBuffer[channel], 0, m_valCount*sizeof(int) );
		m_pHistogramAverage[channel] = 0;
		m_pStandardDeviation[channel] = 0;
		m_pHistogramMax[channel] = PlotPoint();
	}
}

//-----------------------------------------------------------------------------
void HistogramCanvas::OnPaint( wxPaintEvent & )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);
	wxPaintDC dc( this );

	if( !IsActive() )
	{
		return;
	}

	wxCoord w, h;
	dc.GetSize(&w, &h);
	int borderWidth = GetBorderWidth();
	double scaleY = static_cast<double>(h-2*borderWidth)/m_CurrentMax.cnt_;
	double scaleX = static_cast<double>(w-2*borderWidth)/(static_cast<double>(m_valCount) * (static_cast<double>(GetPercentageToDraw()) / 100.));

	dc.SetPen( *wxBLACK );
	dc.SetBrush( *wxWHITE_BRUSH );
	dc.DrawRectangle( 0, 0, w, h );
	dc.SetBrush( wxNullBrush );
	// lower line
	dc.DrawLine( borderWidth, h-borderWidth, w-borderWidth, h-borderWidth );
	// line on left side
	dc.DrawLine( borderWidth, h-borderWidth, borderWidth, borderWidth );

	// markers for X-axis
	int markerStartHeight = h - borderWidth - 3;
	int markerEndHeight = h - borderWidth + 4;
	unsigned int from, to;
	GetDrawRange( &from, &to );
	int XMarkerStepWidth = ( ( to - from ) < 6 ) ? ( to - from ) : ( to - from ) / 6;
	for( unsigned int i=from; i<to; i+=XMarkerStepWidth )
	{
		int xVal = i - from;
		dc.DrawLine( static_cast<int>( borderWidth + ( xVal * scaleX )), markerStartHeight, static_cast<int>( borderWidth + ( xVal * scaleX )), markerEndHeight );
		dc.DrawText( wxString::Format( wxT("%d"), i ), static_cast<int>( borderWidth + ( xVal * scaleX )), h - borderWidth + 3 );
	}

	// markers for y-axis
	int markerStart = borderWidth - 3;
	int markerEnd = borderWidth + 4;
	dc.DrawLine( markerStart, borderWidth, markerEnd, borderWidth );
	wxCoord textWidth, textHeight;
	wxCoord xOffset(1);
	const wxCoord rectSize(10);
	if( m_pHistogramAverage && m_pHistogramMax )
	{
		if( m_boUnsupportedPixelFormat )
		{
			dc.SetTextForeground( *wxRED );
			dc.DrawText( wxString(wxT("Unsupported pixel format")), xOffset, 1 );
		}
		else
		{
			for( int channel=0; channel<m_ChannelCount; channel++ )
			{
				DrawHistogramLine( dc, h, scaleX, scaleY, static_cast<TPlane>(channel) );
				wxString completeDescString = wxString::Format( wxT("%s(%.2f, %5d/%5d"), m_Pens[channel].description_.c_str(), m_pHistogramAverage[channel], m_pHistogramMax[channel].cnt_, m_pHistogramMax[channel].val_ );
				if( m_pStandardDeviation && m_boHandleStandardDeviation )
				{
					completeDescString.Append( wxString::Format( wxT(", %.2f"), m_pStandardDeviation[channel] ) );
				}
				completeDescString.Append( wxT("): ") );
				if( channel != 0 )
				{
					completeDescString.insert( 0, wxString(wxT(", ")) );
				}
				dc.DrawText( completeDescString, xOffset, 1 );
				dc.GetTextExtent( completeDescString, &textWidth, &textHeight );
				wxCoord yOffsetRect(((textHeight-rectSize)/2)+1);
				xOffset += textWidth;
				dc.SetPen( wxPen(*wxBLACK, 1, wxSOLID ) );
				dc.SetBrush( wxBrush(*(m_Pens[channel].pColour_)) );
				dc.DrawRectangle( xOffset, yOffsetRect, rectSize, rectSize );
				dc.SetBrush( wxNullBrush );
				dc.SetPen( wxNullPen );
				xOffset += rectSize + 2;
			}
		}
	}
	dc.SetBrush( wxNullBrush );
	dc.SetPen( wxNullPen );
}

//-----------------------------------------------------------------------------
void HistogramCanvas::SetDrawStepWidth( int value )
//-----------------------------------------------------------------------------
{
	if( ( value < 0 ) || ( value > GetDrawStepWidthMax() ) )
	{
		return;
	}
	AssignDisplayParameter( m_DrawStepWidth, value );
}

IMPLEMENT_DYNAMIC_CLASS(PixelHistogramCanvas, HistogramCanvas)

//-----------------------------------------------------------------------------
PixelHistogramCanvas::PixelHistogramCanvas( wxWindow* parent, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */,
	const wxSize& size /* = wxDefaultSize */, long style /* = wxSUNKEN_BORDER */, const wxString& name /* = "pixelhistogram" */, bool boActive /* = true */ )
	: HistogramCanvas(parent, wxT("Histogram"), id, pos, size, style, name, boActive) {}
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
void PixelHistogramCanvas::RefreshData( const RequestData& data, int x /* = -1 */, int y /* = -1 */, int w /* = -1 */, int h /* = -1 */, bool boForceRefresh /* = false */ )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);

	bool boImageFormatChanged = false;
	const ImageBuffer* pIB = data.image_.getBuffer();
	const int channelCount = ( ( data.bayerParity_ != bmpUndefined ) ? 4 : pIB->iChannelCount );
	if( !MustUpdate( pIB, channelCount, x, y, w, h, boForceRefresh, 0, &boImageFormatChanged ) )
	{
		return;
	}

	if( boImageFormatChanged || !m_ppHistogramBuffer || !m_pHistogramAverage || !m_pHistogramMax || ( m_bayerParity != data.bayerParity_ ) )
	{
		if( data.bayerParity_ == bmpUndefined )
		{
			for( int channel=0; channel<channelCount; channel++ )
			{
				if( channel < PLANE_CNT )
				{
					m_Pens[channel].description_ = ConvertedString(pIB->pChannels[channel].szChannelDesc);
				}
			}
			SetDefaultPens();
		}
		else
		{
			SetBayerDescriptions( data.bayerParity_ );
			SetBayerPens( data.bayerParity_ );
		}
		m_bayerParity = data.bayerParity_;
	}

	PrepareHistogramBuffer( boImageFormatChanged, channelCount, pIB->pixelFormat );

	switch( pIB->pixelFormat )
	{
	case ibpfMono8:
		if( data.bayerParity_ == bmpUndefined )
		{
			for( int line=0; line<m_AOIh; line++ )
			{
				unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					m_pHistogramAverage[pRed] += *pData;
					++m_ppHistogramBuffer[pRed][*pData++];
				}
			}
		}
		else
		{
			for( int line=0; line<m_AOIh; line++ )
			{
				unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					int index = ( ( ( line + m_AOIy ) & 1 ) ? ( ( ( pixel + m_AOIx ) & 1 ) ? pOddOdd : pOddEven ) : ( ( ( pixel + m_AOIx ) & 1 ) ? pEvenOdd : pEvenEven ) );
					m_pHistogramAverage[index] += *pData;
					++m_ppHistogramBuffer[index][*pData++];
				}
			}
		}
		break;
	case ibpfMono10:
	case ibpfMono12:
	case ibpfMono14:
	case ibpfMono16:
		if( data.bayerParity_ == bmpUndefined )
		{
			for( int line=0; line<m_AOIh; line++ )
			{
				unsigned short* pData = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel ));
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					unsigned short value = saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
					m_pHistogramAverage[pRed] += value;
					++m_ppHistogramBuffer[pRed][value];
					++pData;
				}
			}
		}
		else
		{
			for( int line=0; line<m_AOIh; line++ )
			{
				unsigned short* pData = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel ));
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					unsigned short value = saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
					int index = ( ( ( line + m_AOIy ) & 1 ) ? ( ( ( pixel + m_AOIx ) & 1 ) ? pOddOdd : pOddEven ) : ( ( ( pixel + m_AOIx ) & 1 ) ? pEvenOdd : pEvenEven ) );
					m_pHistogramAverage[index] += value;
					++m_ppHistogramBuffer[index][value];
					++pData;
				}
			}
		}
		break;
	case ibpfRGBx888Packed:
	case ibpfRGB888Packed:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				m_pHistogramAverage[pBlue] += pData[0];
				++m_ppHistogramBuffer[pBlue][pData[0]];
				m_pHistogramAverage[pGreen] += pData[1];
				++m_ppHistogramBuffer[pGreen][pData[1]];
				m_pHistogramAverage[pRed] += pData[2];
				++m_ppHistogramBuffer[pRed][pData[2]];
				pData += pIB->iBytesPerPixel;
			}
		}
		break;
	case ibpfRGB101010Packed:
	case ibpfRGB121212Packed:
	case ibpfRGB141414Packed:
	case ibpfRGB161616Packed:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned short* pData = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel ));
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				unsigned short value = saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
				m_pHistogramAverage[pBlue] += value;
				++m_ppHistogramBuffer[pBlue][value];
				++pData;
				value = saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
				m_pHistogramAverage[pGreen] += value;
				++m_ppHistogramBuffer[pGreen][value];
				++pData;
				value = saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
				m_pHistogramAverage[pRed] += value;
				++m_ppHistogramBuffer[pRed][value];
				++pData;
			}
		}
		break;
	case ibpfRGBx888Planar:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pr = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pRed].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pRed].iLinePitch ) + m_AOIx;
			unsigned char* pg = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pGreen].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pGreen].iLinePitch ) + m_AOIx;
			unsigned char* pb = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pBlue].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pBlue].iLinePitch ) + m_AOIx;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				m_pHistogramAverage[pRed] += *pr;
				++m_ppHistogramBuffer[pRed][*pr++];
				m_pHistogramAverage[pGreen] += *pg;
				++m_ppHistogramBuffer[pGreen][*pg++];
				m_pHistogramAverage[pBlue] += *pb;
				++m_ppHistogramBuffer[pBlue][*pb++];
			}
		}
		break;
	case ibpfYUV422Packed:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pY].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
			for( int pixel=0; pixel<m_AOIw/2; pixel++ )
			{
				m_pHistogramAverage[pY] += *pData;
				++m_ppHistogramBuffer[pY][*pData++];
				m_pHistogramAverage[pU] += *pData;
				++m_ppHistogramBuffer[pU][*pData];
				m_pHistogramAverage[pU] += *pData;
				++m_ppHistogramBuffer[pU][*pData++];
				m_pHistogramAverage[pY] += *pData;
				++m_ppHistogramBuffer[pY][*pData++];
				m_pHistogramAverage[pV] += *pData;
				++m_ppHistogramBuffer[pV][*pData];
				m_pHistogramAverage[pV] += *pData;
				++m_ppHistogramBuffer[pV][*pData++];
			}
		}
		break;
	case ibpfYUV422_UYVYPacked:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pY].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
			for( int pixel=0; pixel<m_AOIw/2; pixel++ )
			{
				m_pHistogramAverage[pChannel0] += *pData;
				++m_ppHistogramBuffer[pChannel0][*pData];
				m_pHistogramAverage[pChannel0] += *pData;
				++m_ppHistogramBuffer[pChannel0][*pData++];
				m_pHistogramAverage[pChannel1] += *pData;
				++m_ppHistogramBuffer[pChannel1][*pData++];
				m_pHistogramAverage[pChannel2] += *pData;
				++m_ppHistogramBuffer[pChannel2][*pData];
				m_pHistogramAverage[pChannel2] += *pData;
				++m_ppHistogramBuffer[pChannel2][*pData++];
				m_pHistogramAverage[pChannel1] += *pData;
				++m_ppHistogramBuffer[pChannel1][*pData++];
			}
		}
		break;
	case ibpfYUV422Planar:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* py = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pY].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pY].iLinePitch ) + m_AOIx;
			unsigned char* pu = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pU].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pU].iLinePitch ) + m_AOIx;
			unsigned char* pv = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pV].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pV].iLinePitch ) + m_AOIx;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				m_pHistogramAverage[pY] += *py;
				++m_ppHistogramBuffer[pY][*py++];
				m_pHistogramAverage[pU] += *pu;
				++m_ppHistogramBuffer[pU][*pu];
				m_pHistogramAverage[pV] += *pv;
				++m_ppHistogramBuffer[pV][*pv];
				if( pixel & 1 )
				{
					++pu;
					++pv;
				}
			}
		}
		break;
	default:
		// unsupported color mode
		m_ChannelCount = 0;
		m_boUnsupportedPixelFormat = true;
		break;
	}

	CalculateMaxValue();
	UpdateAnalysisOutput( boImageFormatChanged );
}

IMPLEMENT_DYNAMIC_CLASS(NoiseHistogramCanvas, HistogramCanvas)

//-----------------------------------------------------------------------------
NoiseHistogramCanvas::NoiseHistogramCanvas( wxWindow* parent, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */,
	const wxSize& size /* = wxDefaultSize */, long style /* = wxSUNKEN_BORDER */, const wxString& name /* = "noisehistogram" */, bool boActive /* = true */ )
	: HistogramCanvas(parent, wxT("NoiseHistogram"), id, pos, size, style, name, boActive, true)
//-----------------------------------------------------------------------------
{
	m_Pens[pChannel0Hor].description_ = wxString(wxT("C0Hor"));
	m_Pens[pChannel0Ver].description_ = wxString(wxT("C0Ver"));
	m_Pens[pChannel1Hor].description_ = wxString(wxT("C1Hor"));
	m_Pens[pChannel1Ver].description_ = wxString(wxT("C1Ver"));
	m_Pens[pChannel2Hor].description_ = wxString(wxT("C2Hor"));
	m_Pens[pChannel2Ver].description_ = wxString(wxT("C2Ver"));
}

//-----------------------------------------------------------------------------
void NoiseHistogramCanvas::RefreshData( const RequestData& data, int x /* = -1 */, int y /* = -1 */, int w /* = -1 */, int h /* = -1 */, bool boForceRefresh /* = false */ )
//-----------------------------------------------------------------------------
{
	wxCriticalSectionLocker locker(m_critSect);

	bool boImageFormatChanged = false;
	const ImageBuffer* pIB = data.image_.getBuffer();
	if( !MustUpdate( pIB, 2*pIB->iChannelCount, x, y, w, h, boForceRefresh, 0, &boImageFormatChanged ) )
	{
		return;
	}

	PrepareHistogramBuffer( boImageFormatChanged, 2*pIB->iChannelCount, pIB->pixelFormat );

	switch( pIB->pixelFormat )
	{
	case ibpfMono8:
		{
			unsigned char* pData = 0;
			unsigned char difference = 0;
			for( int line=0; line<m_AOIh; line++ )
			{
				pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[0].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					if( pixel < m_AOIw - 1 )
					{
						difference = abs( *pData - *(pData+1) );
						m_pHistogramAverage[pChannel0Hor] += difference;
						++m_ppHistogramBuffer[pChannel0Hor][difference];
					}
					if( line < m_AOIh - 1 )
					{
						difference = abs( *pData - *(pData+pIB->pChannels[0].iLinePitch) );
						m_pHistogramAverage[pChannel0Ver] += difference;
						++m_ppHistogramBuffer[pChannel0Ver][difference];
					}
					++pData;
				}
			}
		}
		break;
	case ibpfMono10:
	case ibpfMono12:
	case ibpfMono14:
	case ibpfMono16:
		{
			unsigned short* pData = 0;
			unsigned short difference = 0;
			for( int line=0; line<m_AOIh; line++ )
			{
				pData = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[0].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel ));
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					unsigned short current_pixel = saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
					if( pixel < m_AOIw - 1 )
					{
						unsigned short next_pixel_right = saveAssign( *(pData+1), static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
						difference = abs( current_pixel - next_pixel_right );
						m_pHistogramAverage[pChannel0Hor] += difference;
						++m_ppHistogramBuffer[pChannel0Hor][difference];
					}
					if( line < m_AOIh - 1 )
					{
						unsigned short next_pixel_down = saveAssign( *reinterpret_cast<unsigned short*>(reinterpret_cast<unsigned char*>(pData)+pIB->pChannels[0].iLinePitch), static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
						difference = abs( current_pixel - next_pixel_down );
						m_pHistogramAverage[pChannel0Ver] += difference;
						++m_ppHistogramBuffer[pChannel0Ver][difference];
					}
					++pData;
				}
			}
		}
		break;
	case ibpfRGBx888Packed:
	case ibpfRGB888Packed:
		{
			unsigned char* pData = 0;
			unsigned char difference = 0;
			for( int line=0; line<m_AOIh; line++ )
			{
				pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[0].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					if( pixel < m_AOIw - 1 )
					{
						difference = abs( pData[0] - pData[0 + pIB->iBytesPerPixel] );
						m_pHistogramAverage[pChannel0Hor] += difference;
						++m_ppHistogramBuffer[pChannel0Hor][difference];
						difference = abs( pData[1] - pData[1 + pIB->iBytesPerPixel] );
						m_pHistogramAverage[pChannel1Hor] += difference;
						++m_ppHistogramBuffer[pChannel1Hor][difference];
						difference = abs( pData[2] - pData[2 + pIB->iBytesPerPixel] );
						m_pHistogramAverage[pChannel2Hor] += difference;
						++m_ppHistogramBuffer[pChannel2Hor][difference];
					}
					if( line < m_AOIh - 1 )
					{
						difference = abs( pData[0] - pData[0 + pIB->pChannels[0].iLinePitch] );
						m_pHistogramAverage[pChannel0Ver] += difference;
						++m_ppHistogramBuffer[pChannel0Ver][difference];
						difference = abs( pData[1] - pData[1 + pIB->pChannels[0].iLinePitch] );
						m_pHistogramAverage[pChannel1Ver] += difference;
						++m_ppHistogramBuffer[pChannel1Ver][difference];
						difference = abs( pData[2] - pData[2 + pIB->pChannels[0].iLinePitch] );
						m_pHistogramAverage[pChannel2Ver] += difference;
						++m_ppHistogramBuffer[pChannel2Ver][difference];
					}
					pData += pIB->iBytesPerPixel;
				}
			}
		}
		break;
	case ibpfRGB101010Packed:
	case ibpfRGB121212Packed:
	case ibpfRGB141414Packed:
	case ibpfRGB161616Packed:
		{
			unsigned short* pData = 0;
			unsigned short difference = 0;
			for( int line=0; line<m_AOIh; line++ )
			{
				pData = reinterpret_cast<unsigned short*>(static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[0].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel ));
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					
					if( pixel < m_AOIw - 1 )
					{
						difference = abs( saveAssign( pData[0], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) - saveAssign( pData[0 + pIB->iBytesPerPixel], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) );
						m_pHistogramAverage[pChannel0Hor] += difference;
						++m_ppHistogramBuffer[pChannel0Hor][difference];
						difference = abs( saveAssign( pData[1], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) - saveAssign( pData[1 + pIB->iBytesPerPixel], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) );
						m_pHistogramAverage[pChannel1Hor] += difference;
						++m_ppHistogramBuffer[pChannel1Hor][difference];
						difference = abs( saveAssign( pData[2], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) - saveAssign( pData[2 + pIB->iBytesPerPixel], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) );
						m_pHistogramAverage[pChannel2Hor] += difference;
						++m_ppHistogramBuffer[pChannel2Hor][difference];
					}
					if( line < m_AOIh - 1 )
					{
						difference = abs( saveAssign( pData[0], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) - saveAssign( pData[0 + pIB->pChannels[0].iLinePitch], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) );
						m_pHistogramAverage[pChannel0Ver] += difference;
						++m_ppHistogramBuffer[pChannel0Ver][difference];
						difference = abs( saveAssign( pData[1], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) - saveAssign( pData[1 + pIB->pChannels[0].iLinePitch], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) );
						m_pHistogramAverage[pChannel1Ver] += difference;
						++m_ppHistogramBuffer[pChannel1Ver][difference];
						difference = abs( saveAssign( pData[2], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) - saveAssign( pData[2 + pIB->pChannels[0].iLinePitch], static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) ) );
						m_pHistogramAverage[pChannel2Ver] += difference;
						++m_ppHistogramBuffer[pChannel2Ver][difference];
					}
					pData += pIB->iChannelCount;
				}
			}
		}
		break;
	case ibpfRGBx888Planar:
		{
			unsigned char* pr = 0;
			unsigned char* pg = 0;
			unsigned char* pb = 0;
			unsigned char difference = 0;
			for( int line=0; line<m_AOIh; line++ )
			{
				pr = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[0].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[0].iLinePitch ) + m_AOIx;
				pg = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[1].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[1].iLinePitch ) + m_AOIx;
				pb = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[2].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[2].iLinePitch ) + m_AOIx;
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					if( pixel < m_AOIw - 1 )
					{
						difference = abs( pr[0] - pr[1] );
						m_pHistogramAverage[pChannel0Hor] += difference;
						++m_ppHistogramBuffer[pChannel0Hor][difference];
						difference = abs( pg[0] - pg[1] );
						m_pHistogramAverage[pChannel1Hor] += difference;
						++m_ppHistogramBuffer[pChannel1Hor][difference];
						difference = abs( pb[0] - pb[1] );
						m_pHistogramAverage[pChannel2Hor] += difference;
						++m_ppHistogramBuffer[pChannel2Hor][difference];
					}
					if( line < m_AOIh - 1 )
					{
						difference = abs( pr[0] - pr[pIB->pChannels[0].iLinePitch] );
						m_pHistogramAverage[pChannel0Ver] += difference;
						++m_ppHistogramBuffer[pChannel0Ver][difference];
						difference = abs( pg[0] - pg[pIB->pChannels[1].iLinePitch] );
						m_pHistogramAverage[pChannel1Ver] += difference;
						++m_ppHistogramBuffer[pChannel1Ver][difference];
						difference = abs( pb[0] - pb[pIB->pChannels[2].iLinePitch] );
						m_pHistogramAverage[pChannel2Ver] += difference;
						++m_ppHistogramBuffer[pChannel2Ver][difference];
					}
					++pr;
					++pg;
					++pb;
				}
			}
		}
		break;
	case ibpfYUV422Packed:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pY].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
			unsigned char difference = 0;
			for( int pixel=0; pixel<m_AOIw/2; pixel++ )
			{
				bool boIsYV = ( m_AOIx + pixel ) & 1;
				if( pixel < ( m_AOIw - 3 ) / 2 )
				{
					difference = abs( pData[0] - pData[2] );
					m_pHistogramAverage[pChannel0Hor] += difference;
					++m_ppHistogramBuffer[pChannel0Hor][difference];
					difference = abs( pData[2] - pData[4] );
					m_pHistogramAverage[pChannel0Hor] += difference;
					++m_ppHistogramBuffer[pChannel0Hor][difference];
					difference = abs( pData[1] - pData[5] );
					m_pHistogramAverage[boIsYV ? pChannel2Hor : pChannel1Hor] += 2*difference;
					m_ppHistogramBuffer[boIsYV ? pChannel2Hor : pChannel1Hor][difference] += 2;
					difference = abs( pData[3] - pData[7] );
					m_pHistogramAverage[boIsYV ? pChannel1Hor : pChannel2Hor] += 2*difference;
					m_ppHistogramBuffer[boIsYV ? pChannel1Hor : pChannel2Hor][difference] += 2;
				}
				if( line < m_AOIh - 1 )
				{
					difference = abs( pData[0] - pData[pIB->pChannels[0].iLinePitch] );
					m_pHistogramAverage[pChannel0Ver] += difference;
					++m_ppHistogramBuffer[pChannel0Ver][difference];
					difference = abs( pData[1] - pData[1 + pIB->pChannels[0].iLinePitch] );
					m_pHistogramAverage[boIsYV ? pChannel2Ver : pChannel1Ver] += difference;
					++m_ppHistogramBuffer[boIsYV ? pChannel2Ver : pChannel1Ver][difference];
					difference = abs( pData[2] - pData[2 + pIB->pChannels[0].iLinePitch] );
					m_pHistogramAverage[pChannel0Ver] += difference;
					++m_ppHistogramBuffer[pChannel0Ver][difference];
					difference = abs( pData[3] - pData[3 + pIB->pChannels[0].iLinePitch] );
					m_pHistogramAverage[boIsYV ? pChannel1Ver : pChannel2Ver] += difference;
					++m_ppHistogramBuffer[boIsYV ? pChannel1Ver : pChannel2Ver][difference];
				}
				pData += 4;
			}
		}
		break;
	case ibpfYUV422_UYVYPacked:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* pData = static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[pY].iLinePitch ) + ( m_AOIx * pIB->iBytesPerPixel );
			unsigned char difference = 0;
			for( int pixel=0; pixel<m_AOIw/2; pixel++ )
			{
				bool boIsVY = ( m_AOIx + pixel ) & 1;
				if( pixel < ( m_AOIw - 3 ) / 2 )
				{
					difference = abs( pData[1] - pData[3] );
					m_pHistogramAverage[pChannel0Hor] += difference;
					++m_ppHistogramBuffer[pChannel0Hor][difference];
					difference = abs( pData[3] - pData[5] );
					m_pHistogramAverage[pChannel0Hor] += difference;
					++m_ppHistogramBuffer[pChannel0Hor][difference];
					difference = abs( pData[0] - pData[4] );
					m_pHistogramAverage[boIsVY ? pChannel2Hor : pChannel1Hor] += 2*difference;
					m_ppHistogramBuffer[boIsVY ? pChannel2Hor : pChannel1Hor][difference] += 2;
					difference = abs( pData[2] - pData[6] );
					m_pHistogramAverage[boIsVY ? pChannel1Hor : pChannel2Hor] += 2*difference;
					m_ppHistogramBuffer[boIsVY ? pChannel1Hor : pChannel2Hor][difference] += 2;
				}
				if( line < m_AOIh - 1 )
				{
					difference = abs( pData[1] - pData[1 + pIB->pChannels[0].iLinePitch] );
					m_pHistogramAverage[pChannel0Ver] += difference;
					++m_ppHistogramBuffer[pChannel0Ver][difference];
					difference = abs( pData[0] - pData[pIB->pChannels[0].iLinePitch] );
					m_pHistogramAverage[boIsVY ? pChannel2Ver : pChannel1Ver] += difference;
					++m_ppHistogramBuffer[boIsVY ? pChannel2Ver : pChannel1Ver][difference];
					difference = abs( pData[3] - pData[3 + pIB->pChannels[0].iLinePitch] );
					m_pHistogramAverage[pChannel0Ver] += difference;
					++m_ppHistogramBuffer[pChannel0Ver][difference];
					difference = abs( pData[2] - pData[2 + pIB->pChannels[0].iLinePitch] );
					m_pHistogramAverage[boIsVY ? pChannel1Ver : pChannel2Ver] += difference;
					++m_ppHistogramBuffer[boIsVY ? pChannel1Ver : pChannel2Ver][difference];
				}
				pData += 4;
			}
		}
		break;
	case ibpfYUV422Planar:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* py = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pY].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pY].iLinePitch ) + m_AOIx;
			unsigned char* pu = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pU].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pU].iLinePitch ) + m_AOIx;
			unsigned char* pv = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pV].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pV].iLinePitch ) + m_AOIx;
			unsigned char difference = 0;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				if( pixel < m_AOIw - 1 )
				{
					difference = abs( py[0] - py[1] );
					m_pHistogramAverage[pChannel0Hor] += difference;
					++m_ppHistogramBuffer[pChannel0Hor][difference];
					difference = abs( pu[0] - pu[1] );
					m_pHistogramAverage[pChannel1Hor] += difference;
					++m_ppHistogramBuffer[pChannel1Hor][difference];
					difference = abs( pv[0] - pv[1] );
					m_pHistogramAverage[pChannel2Hor] += difference;
					++m_ppHistogramBuffer[pChannel2Hor][difference];
				}
				if( line < m_AOIh - 1 )
				{
					difference = abs( py[0] - py[pIB->pChannels[0].iLinePitch] );
					m_pHistogramAverage[pChannel0Ver] += difference;
					++m_ppHistogramBuffer[pChannel0Ver][difference];
					difference = abs( pu[0] - pu[pIB->pChannels[1].iLinePitch] );
					m_pHistogramAverage[pChannel1Ver] += difference;
					++m_ppHistogramBuffer[pChannel1Ver][difference];
					difference = abs( pv[0] - pv[pIB->pChannels[2].iLinePitch] );
					m_pHistogramAverage[pChannel2Ver] += difference;
					++m_ppHistogramBuffer[pChannel2Ver][difference];
				}
				++py;
				if( pixel & 1 )
				{
					++pu;
					++pv;
				}
			}
		}
		break;
	default:
		// unsupported color mode
		m_ChannelCount = 0;
		m_boUnsupportedPixelFormat = true;
		break;
	}

	CalculateMaxValue();
	UpdateAnalysisOutput( boImageFormatChanged );
}
