#include "LineProfileCanvas.h"

using namespace mvIMPACT::acquire;

IMPLEMENT_DYNAMIC_CLASS(LineProfileCanvas, ImageAnalysisPlotCanvas)

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

//-----------------------------------------------------------------------------
LineProfileCanvas::LineProfileCanvas( wxWindow* parent, wxWindowID id /* = -1 */, const wxPoint& pos /* = wxDefaultPosition */,
	const wxSize& size /* = wxDefaultSize */, long style /* = wxSUNKEN_BORDER */, const wxString& name /* = "line profile" */, bool boActive /* = false */ )
	: ImageAnalysisPlotCanvas( parent, wxT("LineProfile"), id, pos, size, style, name, boActive, 0 ), m_pHorData(0), m_pVerData(0),
	m_horDataCount(0), m_verDataCount(0)
//-----------------------------------------------------------------------------
{
	m_plotFeatures.insert( pfPercentageWindow );
	m_plotFeatures.insert( pfProcessBayerParity );
	SetGridValueFormatString( wxT("%d") );
}

//-----------------------------------------------------------------------------
LineProfileCanvas::~LineProfileCanvas()
//-----------------------------------------------------------------------------
{
	DeallocateProfileBuffer();
}

//-----------------------------------------------------------------------------
void LineProfileCanvas::DeallocateProfileBuffer( void )
//-----------------------------------------------------------------------------
{
	for( int channel=0; channel<m_ChannelCount; channel++ )
	{
		if( m_pHorData )
		{
			delete [] m_pHorData[channel];
		}
		if( m_pVerData )
		{
			delete [] m_pVerData[channel];
		}
	}
	delete [] m_pHorData;
	delete [] m_pVerData;
	m_pHorData = 0;
	m_pVerData = 0;
}

//-----------------------------------------------------------------------------
void LineProfileCanvas::DrawProfileLine( wxPaintDC& dc, int h, int startOffset, double scaleX, double scaleY, int** pData, int elementCount, TPlane plane )
//-----------------------------------------------------------------------------
{
	dc.SetPen( *m_Pens[plane].pColour_ );
	int lowerStart = h - GetBorderWidth();
	unsigned int from, to;
	GetDrawRange( &from, &to );
	for( int i=0; i<elementCount-1; i++ )
	{
		dc.DrawLine( static_cast<int>( startOffset + ( i*scaleX ) ),
					 static_cast<int>( lowerStart - ( saveAssign( pData[plane][i] - static_cast<int>(from), 0, static_cast<int>(to - from) ) * scaleY ) ),
					 static_cast<int>( startOffset + ( ( i+1 ) * scaleX ) ),
					 static_cast<int>( lowerStart - ( saveAssign( pData[plane][i+1] - static_cast<int>(from), 0, static_cast<int>(to - from) ) * scaleY ) ) );
	}
	dc.SetPen( wxNullPen );
}

//-----------------------------------------------------------------------------
wxString LineProfileCanvas::GetGridValue( int row, int col ) const
//-----------------------------------------------------------------------------
{
	if( m_pHorData && m_pVerData && ( col >= 0 ) && ( row >= 0 ) )
	{
		if( ( col >= m_ChannelCount ) && ( col < 2*m_ChannelCount ) )
		{
			// vertical data
			if( row == 0 )
			{
				return wxString(m_Pens[col%m_ChannelCount].description_ + wxString(wxT("(ver.)")) );
			}
			else if( row <= m_verDataCount )
			{
				return wxString::Format( GetGridValueFormatString().c_str(), m_pVerData[col%m_ChannelCount][row-1] );
			}
		}
		else if( col < m_ChannelCount )
		{
			// horizontal data
			if( row == 0 )
			{
				return wxString(m_Pens[col].description_ + wxString(wxT("(hor.)")) );
			}
			else if( row <= m_horDataCount )
			{
				return wxString::Format( GetGridValueFormatString().c_str(), m_pHorData[col][row-1] );
			}
		}
	}
	return wxEmptyString;
}

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

	if( !IsActive() )
	{
		return;
	}

	wxCoord w, h;
	dc.GetSize(&w, &h);

	int borderWidth = GetBorderWidth();
	int diagramWidth = static_cast<int>(( w - ( 3 * borderWidth ) + 1.0 ) / 2.0 );
	int diagramHeight = h - ( 2 * borderWidth );
	int verDiagramStart = (2 * borderWidth ) + diagramWidth;
	double scaleY = static_cast<double>(diagramHeight) / ( static_cast<double>(m_valCount) * static_cast<double>(GetPercentageToDraw()) / 100. );

	dc.SetPen( *wxTRANSPARENT_PEN );
	dc.SetBrush( *wxWHITE_BRUSH );
	dc.DrawRectangle( borderWidth + 1, borderWidth, diagramWidth + 1, diagramHeight );
	dc.DrawRectangle( verDiagramStart + 1, borderWidth, diagramWidth + 1, diagramHeight );

	unsigned int from, to;
	GetDrawRange( &from, &to );
	// hor. profile
	double scaleX = static_cast<double>(diagramWidth) / static_cast<double>( ( m_horDataCount == 0 ) ? 1 : m_horDataCount - 1);
	dc.SetPen( *wxBLACK );
	dc.DrawLine( borderWidth, h-borderWidth, borderWidth, borderWidth - 1 );
	dc.DrawLine( borderWidth, h-borderWidth, borderWidth + diagramWidth + 2, h-borderWidth );
	dc.DrawText( wxString::Format( wxT("%d"), m_horDataCount - 1 ), diagramWidth, h - borderWidth + 3 );
	dc.DrawText( wxString::Format( wxT("%d"), to ), borderWidth/2, 1 );
	wxCoord xOffset(borderWidth + 30);
	wxCoord yOffset(h - borderWidth + 3);
	dc.DrawText( wxString::Format( wxT("%d"), from ), borderWidth/2, yOffset );
	dc.DrawText( wxT("hor. profile"), diagramWidth/2, 1 );
	if( m_boUnsupportedPixelFormat )
	{
		dc.SetTextForeground( *wxRED );
		dc.DrawText( wxString(wxT("Unsupported pixel format")), xOffset, yOffset );
		dc.SetTextForeground( *wxBLACK );
	}
	else if( m_pHorData )
	{
		const wxCoord rectSize(10);
		for( int channel=0; channel<m_ChannelCount; channel++ )
		{
			DrawProfileLine( dc, h, borderWidth + 1, scaleX, scaleY, m_pHorData, m_horDataCount, static_cast<TPlane>(channel) );
			wxString completeDescString = wxString::Format( wxT("%s: "), m_Pens[channel].description_.c_str() );
			if( channel != 0 )
			{
				completeDescString.insert( 0, wxString(wxT(", ")) );
			}
			dc.DrawText( completeDescString, xOffset, yOffset );
			wxCoord textWidth, textHeight;
			dc.GetTextExtent( completeDescString, &textWidth, &textHeight );
			xOffset += textWidth;
			dc.SetPen( wxPen(*wxBLACK, 1, wxSOLID ) );
			dc.SetBrush( wxBrush(*(m_Pens[channel].pColour_)) );
			dc.DrawRectangle( xOffset, yOffset+3, rectSize, rectSize );
			dc.SetBrush( wxNullBrush );
			dc.SetPen( wxNullPen );
			xOffset += rectSize + 2;
		}
	}

	// ver. profile
	scaleX = static_cast<double>(diagramWidth) / static_cast<double>( ( m_verDataCount == 0 ) ? 1 : m_verDataCount - 1);
	dc.SetPen( *wxBLACK );
	dc.DrawLine( verDiagramStart, h-borderWidth, verDiagramStart, borderWidth - 1 );
	dc.DrawLine( verDiagramStart, h-borderWidth, verDiagramStart + diagramWidth + 2, h-borderWidth );
	dc.DrawText( wxString::Format( wxT("%d"), m_verDataCount - 1 ), borderWidth + 2 * diagramWidth, h - borderWidth + 3 );
	dc.DrawText( wxString::Format( wxT("%d"), to ), static_cast<int>(1.5*static_cast<double>(borderWidth) + diagramWidth + 1), 1 );
	dc.DrawText( wxString::Format( wxT("%d"), from ), static_cast<int>(1.5*static_cast<double>(borderWidth) + diagramWidth + 1), yOffset );
	dc.DrawText( wxT("ver. profile"), static_cast<int>(borderWidth + ( 1.5 * static_cast<double>(diagramWidth) )), 1 );
	if( m_boUnsupportedPixelFormat )
	{
		dc.SetTextForeground( *wxRED );
		dc.DrawText( wxString(wxT("Unsupported pixel format")), xOffset + verDiagramStart, yOffset );
		dc.SetTextForeground( *wxBLACK );
	}
	else if( m_pVerData )
	{
		for( int channel=0; channel<m_ChannelCount; channel++ )
		{
			DrawProfileLine( dc, h, verDiagramStart + 1, scaleX, scaleY, m_pVerData, m_verDataCount, static_cast<TPlane>(channel) );
		}
	}
	dc.SetBrush( wxNullBrush );
	dc.SetPen( wxNullPen );
}


//-----------------------------------------------------------------------------
void LineProfileCanvas::ProcessRGB_8u_CxData( const ImageBuffer* pIB, const int inc, const int order[3] )
//-----------------------------------------------------------------------------
{
	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_pHorData[pBlue][pixel] += pData[order[0]];
			m_pVerData[pBlue][line] += pData[order[0]];
			m_pHorData[pGreen][pixel] += pData[order[1]];
			m_pVerData[pGreen][line] += pData[order[1]];
			m_pHorData[pRed][pixel] += pData[order[2]];
			m_pVerData[pRed][line] += pData[order[2]];
			pData += inc;
		}
	}
}

//-----------------------------------------------------------------------------
template<typename _Ty>
void LineProfileCanvas::ProcessYUVData( const ImageBuffer* pIB )
//-----------------------------------------------------------------------------
{
	if( pIB->iWidth > 1 )
	{
		for( int line=0; line<m_AOIh; line++ )
		{
			_Ty* pData = reinterpret_cast<_Ty*>(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_pHorData[pY][pixel] += *pData;
				m_pVerData[pY][line] += *pData;
				if( ( m_AOIx + pixel ) & 1 )
				{
					m_pHorData[pV][pixel] += *(pData + 1);
					m_pVerData[pV][line] += *(pData + 1);
					m_pHorData[pU][pixel] += *(pData - 1);
					m_pVerData[pU][line] += *(pData - 1);
				}
				else
				{
					m_pHorData[pV][pixel] += *(pData + 3);
					m_pVerData[pV][line] += *(pData + 3);
					m_pHorData[pU][pixel] += *(pData + 1);
					m_pVerData[pU][line] += *(pData + 1);
				}
				pData += 2;
			}
		}
	}
}

//-----------------------------------------------------------------------------
template<typename _Ty>
void LineProfileCanvas::ProcessYUV444Data( const ImageBuffer* pIB )
//-----------------------------------------------------------------------------
{
	for( int line=0; line<m_AOIh; line++ )
	{
		_Ty* pData = reinterpret_cast<_Ty*>(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_pHorData[pChannel0][pixel] += pData[pChannel0];
			m_pVerData[pChannel0][line] += pData[pChannel0];
			m_pHorData[pChannel1][pixel] += pData[pChannel1];
			m_pVerData[pChannel1][line] += pData[pChannel1];
			m_pHorData[pChannel2][pixel] += pData[pChannel2];
			m_pVerData[pChannel2][line] += pData[pChannel2];
			pData += 3;
		}
	}
}

//-----------------------------------------------------------------------------
template<typename _Ty>
void LineProfileCanvas::ProcessUYVData( const ImageBuffer* pIB )
//-----------------------------------------------------------------------------
{
	if( pIB->iWidth > 1 )
	{
		for( int line=0; line<m_AOIh; line++ )
		{
			_Ty* pData = reinterpret_cast<_Ty*>(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_pHorData[pChannel1][pixel] += *(pData + 1);
				m_pVerData[pChannel1][line] += *(pData +1);
				if( ( m_AOIx + pixel ) & 1 )
				{
					m_pHorData[pChannel2][pixel] += *pData;
					m_pVerData[pChannel2][line] += *pData;
					m_pHorData[pChannel0][pixel] += *(pData - 2);
					m_pVerData[pChannel0][line] += *(pData - 2);
				}
				else
				{
					m_pHorData[pChannel2][pixel] += *(pData + 2);
					m_pVerData[pChannel2][line] += *(pData + 2);
					m_pHorData[pChannel0][pixel] += *pData;
					m_pVerData[pChannel0][line] += *pData;
				}
				pData += 2;
			}
		}
	}
}

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

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

	if( boImageFormatChanged || ( m_bayerParity != bayerParity ) )
	{
		SetupGDIData( pIB, channelCount, bayerParity );
	}

	if( boAOIChanged || !m_pHorData || !m_pVerData || boImageFormatChanged )
	{
		// free old memory
		DeallocateProfileBuffer();
		// allocate new memory
		m_pHorData = new int*[channelCount];
		m_pVerData = new int*[channelCount];
		for( int channel=0; channel<channelCount; channel++ )
		{
			m_horDataCount = ( bayerParity != bmpUndefined ) ? ( ( m_AOIw + 1 ) / 2 ): m_AOIw;
			m_pHorData[channel] = new int[m_horDataCount];
			m_verDataCount = ( bayerParity != bmpUndefined ) ? ( ( m_AOIh + 1 ) / 2 ): m_AOIh;
			m_pVerData[channel] = new int[m_verDataCount];
		}

		if( m_pNumericalDisplay )
		{
			int columnCount = m_pNumericalDisplay->GetTable()->GetNumberCols();
			int columnsNeeded = 2*channelCount;
			if( columnCount < columnsNeeded )
			{
				m_pNumericalDisplay->AppendCols( columnsNeeded - columnCount );
			}
			else if( columnCount > columnsNeeded )
			{
				m_pNumericalDisplay->DeleteCols( columnCount - 1, columnCount - columnsNeeded );
			}

			int rowCount = m_pNumericalDisplay->GetTable()->GetNumberRows();
			int rowsNeeded = 1 + ( ( m_horDataCount > m_verDataCount ) ? m_horDataCount : m_verDataCount );
			if( rowCount < rowsNeeded )
			{
				m_pNumericalDisplay->AppendRows( rowsNeeded - rowCount );
			}
			else if( rowCount > rowsNeeded )
			{
				m_pNumericalDisplay->DeleteRows( rowsNeeded - 1, rowCount - rowsNeeded );
			}
			for( int i=0; i<channelCount; i++ )
			{
				m_pNumericalDisplay->SetCellBackgroundColour( 0, i               , *m_Pens[i].pColour_ );
				m_pNumericalDisplay->SetCellBackgroundColour( 0, i + channelCount, *m_Pens[i].pColour_ );
				m_pNumericalDisplay->SetCellTextColour( 0, i               , *wxWHITE );
				m_pNumericalDisplay->SetCellTextColour( 0, i + channelCount, *wxWHITE );
			}
		}
	}

	assert( ( channelCount <= PLANE_CNT ) && wxT("A pixel format defines more channels then this tool can handle") );
	for( int channel=0; channel<channelCount; channel++ )
	{
		memset( m_pHorData[channel], 0, sizeof(int)*m_horDataCount );
		memset( m_pVerData[channel], 0, sizeof(int)*m_verDataCount );
	}

	m_bayerParity = bayerParity;
	m_ChannelCount = channelCount;

	switch( pIB->pixelFormat )
	{
	case ibpfMono10:
	case ibpfBGR101010Packed_V2:
	case ibpfRGB101010Packed:
	case ibpfYUV422_10Packed:
	case ibpfYUV422_UYVY_10Packed:
	case ibpfYUV444_UYV_10Packed:
	case ibpfYUV444_10Packed:
		m_valCount = VAL_COUNT_10_BIT;
		break;
	case ibpfRGB121212Packed:
	case ibpfMono12:
	case ibpfMono12Packed_V2:
		m_valCount = VAL_COUNT_12_BIT;
		break;
	case ibpfRGB141414Packed:
	case ibpfMono14:
		m_valCount = VAL_COUNT_14_BIT;
		break;
	case ibpfRGB161616Packed:
	case ibpfMono16:
		m_valCount = VAL_COUNT_16_BIT;
		break;
	case ibpfRGBx888Packed:
	case ibpfBGR888Packed:
	case ibpfRGB888Packed:
	case ibpfRGBx888Planar:
	case ibpfYUV422Packed:
	case ibpfYUV422_UYVYPacked:
	case ibpfYUV444_UYVPacked:
	case ibpfYUV444Packed:
	case ibpfYUV422Planar:
	case ibpfMono8:
	default:
		m_valCount = VAL_COUNT_8_BIT;
		break;
	}

	// calculate data
	switch( pIB->pixelFormat )
	{
	case ibpfMono8:
		if( 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;
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					m_pHorData[pRed][pixel] += *pData;
					m_pVerData[pRed][line] += *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;
				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_pHorData[index][pixel/2] += *pData;
					m_pVerData[index][line/2] += *pData++;
				}
			}
		}
		break;
	case ibpfMono10:
	case ibpfMono12:
	case ibpfMono14:
	case ibpfMono16:
		if( bayerParity == bmpUndefined )
		{
			for( int line=0; line<m_AOIh; line++ )
			{
				unsigned short* pData = static_cast<unsigned short*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->iWidth ) + m_AOIx;
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					m_pHorData[pRed][pixel] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
					m_pVerData[pRed][line] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
					++pData;
				}
			}
		}
		else
		{
			for( int line=0; line<m_AOIh; line++ )
			{
				unsigned short* pData = static_cast<unsigned short*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->iWidth ) + m_AOIx;
				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_pHorData[index][pixel/2] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
					m_pVerData[index][line/2] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
					++pData;
				}
			}
		}
		break;
	case ibpfMono12Packed_V2:
		if( bayerParity == bmpUndefined )
		{
			for( int line=0; line<m_AOIh; line++ )
			{
				const int pixOffset = ( m_AOIy + line ) * pIB->iWidth;
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					unsigned short value = GetMonoPacked_V2Pixel( static_cast<unsigned char*>(pIB->vpData), pixOffset + m_AOIx + pixel, 4 );
					m_pHorData[pRed][pixel] += value;
					m_pVerData[pRed][line] += value;
				}
			}
		}
		else
		{
			for( int line=0; line<m_AOIh; line++ )
			{
				const int pixOffset = ( m_AOIy + line ) * pIB->iWidth;
				for( int pixel=0; pixel<m_AOIw; pixel++ )
				{
					unsigned short value = GetMonoPacked_V2Pixel( static_cast<unsigned char*>(pIB->vpData), pixOffset + m_AOIx + pixel, 4 );
					int index = ( ( ( line + m_AOIy ) & 1 ) ? ( ( ( pixel + m_AOIx ) & 1 ) ? pOddOdd : pOddEven ) : ( ( ( pixel + m_AOIx ) & 1 ) ? pEvenOdd : pEvenEven ) );
					m_pHorData[index][pixel/2] += value;
					m_pVerData[index][line/2] += value;
				}
			}
		}
		break;
	case ibpfRGBx888Packed:
		{
			int order[3] = { 0, 1, 2 };
			ProcessRGB_8u_CxData( pIB, 4, order );
		}
		break;
	case ibpfBGR888Packed:
		{
			int order[3] = { 2, 1, 0 };
			ProcessRGB_8u_CxData( pIB, 3, order );
		}
		break;
	case ibpfRGB888Packed:
		{
			int order[3] = { 0, 1, 2 };
			ProcessRGB_8u_CxData( pIB, 3, order );
		}
		break;
	case ibpfBGR101010Packed_V2:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned short red, green, blue;
			unsigned int* p =  reinterpret_cast<unsigned int*>(static_cast<unsigned char*>(pIB->vpData) + ( ( m_AOIy + line ) * pIB->pChannels[0].iLinePitch )) + m_AOIx;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				GetBGR101010Packed_V2Pixel( *p++, red, green, blue );
				m_pHorData[pBlue][pixel] += blue;
				m_pVerData[pBlue][line] += blue;
				m_pHorData[pGreen][pixel] += green;
				m_pVerData[pGreen][line] += green;
				m_pHorData[pRed][pixel] += red;
				m_pVerData[pRed][line] += red;
			}
		}
		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++ )
			{
				m_pHorData[pBlue][pixel] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
				m_pVerData[pBlue][line] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
				++pData;
				m_pHorData[pGreen][pixel] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
				m_pVerData[pGreen][line] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
				++pData;
				m_pHorData[pRed][pixel] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
				m_pVerData[pRed][line] += saveAssign( *pData, static_cast<unsigned short>(0), static_cast<unsigned short>(m_valCount-1) );
				++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_pHorData[pRed][pixel] += *pr;
				m_pVerData[pRed][line] += *pr++;
				m_pHorData[pGreen][pixel] += *pg;
				m_pVerData[pGreen][line] += *pg++;
				m_pHorData[pBlue][pixel] += *pb;
				m_pVerData[pBlue][line] += *pb++;
			}
		}
		break;
	case ibpfYUV422Packed:
		ProcessYUVData<unsigned char>( pIB );
		break;
	case ibpfYUV422_10Packed:
		ProcessYUVData<unsigned short>( pIB );
		break;
	case ibpfYUV422_UYVYPacked:
		ProcessUYVData<unsigned char>( pIB );
		break;
	case ibpfYUV422_UYVY_10Packed:
		ProcessUYVData<unsigned short>( pIB );
		break;
	case ibpfYUV444_UYVPacked:
		ProcessYUV444Data<unsigned char>( pIB );
		break;
	case ibpfYUV444_UYV_10Packed:
		ProcessYUV444Data<unsigned short>( pIB );
		break;
	case ibpfYUV444Packed:
		ProcessYUV444Data<unsigned char>( pIB );
		break;
	case ibpfYUV444_10Packed:
		ProcessYUV444Data<unsigned short>( pIB );
		break;
	case ibpfYUV422Planar:
		for( int line=0; line<m_AOIh; line++ )
		{
			unsigned char* py = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pRed].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pY].iLinePitch ) + m_AOIx;
			unsigned char* pu = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pGreen].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pU].iLinePitch ) + m_AOIx;
			unsigned char* pv = static_cast<unsigned char*>(pIB->vpData) + pIB->pChannels[pBlue].iChannelOffset + ( ( m_AOIy + line ) * pIB->pChannels[pV].iLinePitch ) + m_AOIx;
			for( int pixel=0; pixel<m_AOIw; pixel++ )
			{
				m_pHorData[pY][pixel] += *py;
				m_pVerData[pY][line] += *py++;
				m_pHorData[pU][pixel] += *pu;
				m_pVerData[pU][line] += *pu;
				m_pHorData[pV][pixel] += *pv;
				m_pVerData[pV][line] += *pv;
				if( pixel & 1 )
				{
					++pu;
					++pv;
				}
			}
		}
		break;
	default:
		// unsupported colour format
		m_boUnsupportedPixelFormat = true;
		break;
	}

	/// \todo This is not correct for bayer raw data with AOI with a AOIw or AOIh that is not diviable by 2
	for( int channel=0; channel<m_ChannelCount; channel++ )
	{
		for( int i=0; i<m_horDataCount; i++ )
		{
			m_pHorData[channel][i] /= m_verDataCount;
		}
		for( int i=0; i<m_verDataCount; i++ )
		{
			m_pVerData[channel][i] /= m_horDataCount;
		}
	}

	bool boFullUpdate = boAOIChanged || boImageFormatChanged;
	UpdateAnalysisOutput( boFullUpdate );
}
