/////////////////////////////////////////////////////////////////////////////////////////////////////
//																																	//
//	Coded by www.decotechpro.com, all rights reserved.  Created: 1999-01-01©  Modified: 2016-03-22©	//
//																																	//
// JCH_PixelMap.cpp : Implementation of JCH_PixelMap class & functions.										//
//																																	//
/////////////////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "TT_PixelMap.h"

#include "afx.h"

/////////////////////////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////////////////////////////
//																																	//
//	Coded by www.adeluc.com   All rights reserved.    Created: 1999-02-06©   Modified: 2004-11-29©	//
//																																	//
//	Purpose:	Definition of the constructor.																			//
//																																	//
//	Result:																														//
//																																	//
/////////////////////////////////////////////////////////////////////////////////////////////////////

JCH_PixelMap::JCH_PixelMap()
{ 
	m_pFirstPixel  = 0;
	m_unMemorySize	= 0;

	ASSERT( sizeof( JCH_Pixel ) == 4 );

	m_BitMapInfo.bmiHeader.biSize				= sizeof( BITMAPINFOHEADER );
	m_BitMapInfo.bmiHeader.biWidth			= 0;
	m_BitMapInfo.bmiHeader.biHeight			= 0;
	m_BitMapInfo.bmiHeader.biPlanes			= 1;
	m_BitMapInfo.bmiHeader.biBitCount		= (4 * 8);
	m_BitMapInfo.bmiHeader.biCompression	= BI_RGB;
	m_BitMapInfo.bmiHeader.biSizeImage		= 0;
	m_BitMapInfo.bmiHeader.biXPelsPerMeter	= rdvPixelsPerScreenMeter;
	m_BitMapInfo.bmiHeader.biYPelsPerMeter	= rdvPixelsPerScreenMeter;
	m_BitMapInfo.bmiHeader.biClrUsed			= 0;
	m_BitMapInfo.bmiHeader.biClrImportant	= 0;
	
	m_BitMapInfo.bmiColors[0].rgbBlue	  = 0;
	m_BitMapInfo.bmiColors[0].rgbGreen	  = 0;
	m_BitMapInfo.bmiColors[0].rgbRed		  = 0;
	m_BitMapInfo.bmiColors[0].rgbReserved = 0;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
//																																	//
//	Coded by www.adeluc.com   All rights reserved.    Created: 1999-02-06©   Modified: 2014-03-01©	//
//																																	//
//	Purpose:	Definition of the copy constructor.																		//
//																																	//
//	Result:																														//
//																																	//
//	Note:	If the memory can not be allocated, 'this' is returned unchanged.									//
//																																	//
/////////////////////////////////////////////////////////////////////////////////////////////////////

JCH_PixelMap::JCH_PixelMap( const JCH_PixelMap & rcPixelMap )	//	throw( CMemoryException * )
{ 
	JCH_Pixel  *ppixFirstPixel	= 0;
	
	if ( (rcPixelMap.m_unMemorySize == 0) || 
		((ppixFirstPixel = (JCH_Pixel *)new unsigned char[rcPixelMap.m_unMemorySize]) != 0) )
	{
		m_pFirstPixel = 0;	// Must be initialized before calling AttachMemory().
		AttachMemory( ppixFirstPixel, rcPixelMap.m_unMemorySize,
							rcPixelMap.m_BitMapInfo.bmiHeader.biWidth,
							rcPixelMap.m_BitMapInfo.bmiHeader.biHeight );
		
		m_BitMapInfo.bmiHeader.biSize				= rcPixelMap.m_BitMapInfo.bmiHeader.biSize;
		m_BitMapInfo.bmiHeader.biPlanes			= rcPixelMap.m_BitMapInfo.bmiHeader.biPlanes;
		m_BitMapInfo.bmiHeader.biBitCount		= rcPixelMap.m_BitMapInfo.bmiHeader.biBitCount;
		m_BitMapInfo.bmiHeader.biCompression	= rcPixelMap.m_BitMapInfo.bmiHeader.biCompression;
		m_BitMapInfo.bmiHeader.biSizeImage		= rcPixelMap.m_BitMapInfo.bmiHeader.biSizeImage;
		m_BitMapInfo.bmiHeader.biXPelsPerMeter	= rcPixelMap.m_BitMapInfo.bmiHeader.biXPelsPerMeter;
		m_BitMapInfo.bmiHeader.biYPelsPerMeter	= rcPixelMap.m_BitMapInfo.bmiHeader.biYPelsPerMeter;
		m_BitMapInfo.bmiHeader.biClrUsed			= rcPixelMap.m_BitMapInfo.bmiHeader.biClrUsed;
		m_BitMapInfo.bmiHeader.biClrImportant	= rcPixelMap.m_BitMapInfo.bmiHeader.biClrImportant;
		
		m_BitMapInfo.bmiColors[0].rgbBlue	  = rcPixelMap.m_BitMapInfo.bmiColors[0].rgbBlue;
		m_BitMapInfo.bmiColors[0].rgbGreen	  = rcPixelMap.m_BitMapInfo.bmiColors[0].rgbGreen;
		m_BitMapInfo.bmiColors[0].rgbRed		  = rcPixelMap.m_BitMapInfo.bmiColors[0].rgbRed;
		m_BitMapInfo.bmiColors[0].rgbReserved = rcPixelMap.m_BitMapInfo.bmiColors[0].rgbReserved;
		
		if ( m_pFirstPixel != 0 ) memcpy( m_pFirstPixel, rcPixelMap.m_pFirstPixel, m_unMemorySize );
	}
	else AfxThrowMemoryException();
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
//																																	//
//	Coded by www.adeluc.com   All rights reserved.    Created: 1999-02-06©   Modified: 2014-03-01©	//
//																																	//
//	Purpose:	Definition of the operator =.																				//
//																																	//
//	Result:																														//
//																																	//
//	Note:	If the memory can not be allocated, 'this' is returned without its previous memory.			//
//																																	//
/////////////////////////////////////////////////////////////////////////////////////////////////////

JCH_PixelMap & JCH_PixelMap::operator = ( const JCH_PixelMap & rcPixelMap )	//	throw( CMemoryException * )
{	 
	if ( this != &rcPixelMap )
	{
		if ( AllocatePixels( rcPixelMap.GetWidth(), rcPixelMap.GetHeight()) )
		{
			m_BitMapInfo.bmiHeader.biSize				= rcPixelMap.m_BitMapInfo.bmiHeader.biSize;
			m_BitMapInfo.bmiHeader.biPlanes			= rcPixelMap.m_BitMapInfo.bmiHeader.biPlanes;
			m_BitMapInfo.bmiHeader.biBitCount		= rcPixelMap.m_BitMapInfo.bmiHeader.biBitCount;
			m_BitMapInfo.bmiHeader.biCompression	= rcPixelMap.m_BitMapInfo.bmiHeader.biCompression;
			m_BitMapInfo.bmiHeader.biSizeImage		= rcPixelMap.m_BitMapInfo.bmiHeader.biSizeImage;
			m_BitMapInfo.bmiHeader.biXPelsPerMeter	= rcPixelMap.m_BitMapInfo.bmiHeader.biXPelsPerMeter;
			m_BitMapInfo.bmiHeader.biYPelsPerMeter	= rcPixelMap.m_BitMapInfo.bmiHeader.biYPelsPerMeter;
			m_BitMapInfo.bmiHeader.biClrUsed			= rcPixelMap.m_BitMapInfo.bmiHeader.biClrUsed;
			m_BitMapInfo.bmiHeader.biClrImportant	= rcPixelMap.m_BitMapInfo.bmiHeader.biClrImportant;
			
			m_BitMapInfo.bmiColors[0].rgbBlue	  = rcPixelMap.m_BitMapInfo.bmiColors[0].rgbBlue;
			m_BitMapInfo.bmiColors[0].rgbGreen	  = rcPixelMap.m_BitMapInfo.bmiColors[0].rgbGreen;
			m_BitMapInfo.bmiColors[0].rgbRed		  = rcPixelMap.m_BitMapInfo.bmiColors[0].rgbRed;
			m_BitMapInfo.bmiColors[0].rgbReserved = rcPixelMap.m_BitMapInfo.bmiColors[0].rgbReserved;

			if ( m_pFirstPixel != 0 ) memcpy( m_pFirstPixel, rcPixelMap.m_pFirstPixel, m_unMemorySize );
		}
		else AfxThrowMemoryException();
	}
							
	return( *this );
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
//																																	//
//	Coded by www.adeluc.com   All rights reserved.    Created: 1999-02-06©   Modified: 2004-12-01©	//
//																																	//
//	Purpose:	Allocate the memory to store all the pixels.															//
//																																	//
//	(0 <= 'cnWidth') && (0 <= 'cnHeight').																				//
//																																	//
//	'cbFillNewArea': If set to FALSE then 'cpixFillValue' is ignored.											//
//																																	//
//	Result: If memory is allocated successfully, 'm_pFirstPixel', 'm_unMemorySize',						//
//			  'm_BitMapInfo.bmiHeader.biWidth' and 'm_BitMapInfo.bmiHeader.biHeight' are set and		//
//			  TRUE is returned. Otherwise nothing is changed and FALSE is returned.							//
//																																	//
// Note: If 'm_pFirstPixel' does not already point to the exact required memory size, then			//
//			this memory will be automatically freed and re-allocated.											//
//																																	//
/////////////////////////////////////////////////////////////////////////////////////////////////////

bool JCH_PixelMap::AllocatePixels( const int cnWidth, const int cnHeight, 
											  const bool cbFillNewArea, const JCH_Pixel cpixFillValue )
{ 
	bool	bStatus = FALSE;

   //------------------------------------------------

	if ( (cnWidth <= 0) || (cnHeight <= 0) )
	{
		FreePixels();
		bStatus = TRUE;
	}

	else
	{
		JCH_Pixel	  *ppixFirstPixel;
		unsigned int	unMemorySize = (cnWidth * sizeof( JCH_Pixel ) * cnHeight);

		if ( unMemorySize == m_unMemorySize )
		{
			m_BitMapInfo.bmiHeader.biWidth  = cnWidth;	
			m_BitMapInfo.bmiHeader.biHeight = cnHeight;				
			bStatus = TRUE;
		}
		else
		{
			FreePixels();	// Give a chance to the memory pool!
			
			if ( (ppixFirstPixel = (JCH_Pixel *)new unsigned char[unMemorySize]) != 0 )	// Allocation successful?
			{
				AttachMemory( ppixFirstPixel, unMemorySize, cnWidth, cnHeight );
				bStatus = TRUE;
			}
		}

		if ( bStatus && cbFillNewArea ) Fill( cpixFillValue );
	}

	return( bStatus );
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
//																																	//
//	Coded by www.adeluc.com   All rights reserved.    Created: 2004-11-29©   Modified: 2004-11-29©	//
//																																	//
//	Purpose:	Attach memory pointed by 'pFirstPixel' to this object.											//
//																																	//
// Note: For the function to success, 'pFirstPixel', 'unMemorySize', 'cnWidth' and 'cnHeight'		//
//			must all equal 0 or must all be different from 0.														//
//																																	//
//	(0 <= 'cnWidth') && (0 <= 'cnHeight').																				//
//																																	//
//	'cnXPelsPerMeter': Ignored if it is set to -1.																	//
//																																	//
//	'cnYPelsPerMeter': Ignored if it is set to -1.																	//
//																																	//
//	Result: Returns FALSE if this object is already attached (i.e. this->m_pFirstPixel != 0) or		//
//			  if the arguments are invalid.  Otherwise returns TRUE.												//
//																																	//
/////////////////////////////////////////////////////////////////////////////////////////////////////

bool JCH_PixelMap::AttachMemory( JCH_Pixel *pFirstPixel, unsigned int unMemorySize, const int cnWidth,
										  const int cnHeight, const int cnXPelsPerMeter, const int cnYPelsPerMeter )
{
	if ( (((pFirstPixel != 0) && (unMemorySize != 0) && (cnWidth > 0) && (cnHeight > 0)) ||
			((pFirstPixel == 0) && (unMemorySize == 0) && (cnWidth == 0) && (cnHeight == 0))) && (m_pFirstPixel == 0) )
	{
		m_pFirstPixel  = pFirstPixel;
		m_unMemorySize = unMemorySize;				
		m_BitMapInfo.bmiHeader.biWidth  = cnWidth;	
		m_BitMapInfo.bmiHeader.biHeight = cnHeight;				

		if ( cnXPelsPerMeter != -1 ) SetXPelsPerMeter( cnXPelsPerMeter );
		if ( cnYPelsPerMeter != -1 ) SetYPelsPerMeter( cnYPelsPerMeter );

		return( TRUE );
	}
	else return( FALSE );
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
//																																	//
//	Coded by www.adeluc.com   All rights reserved.    Created: 2004-11-29©   Modified: 2004-11-29©	//
//																																	//
//	Purpose:	Detach memory pointed by 'm_pFirstPixel' from this object.										//
//																																	//
//	IMPORTANT: The memory pointed by 'm_pFirstPixel' is NOT freed.												//
//																																	//
//	'punMemorySize': May be NULL if you do not need this information.											//
//																																	//
//	'pnWidth': May be NULL if you do not need this information.													//
//																																	//
//	'pnHeight': May be NULL if you do not need this information.												//
//																																	//
//	'pnXPelsPerMeter': May be NULL if you do not need this information.										//
//																																	//
//	'pnYPelsPerMeter': May be NULL if you do not need this information.										//
//																																	//
//	Result: Returns current 'm_pFirstPixel' and set 'm_pFirstPixel', 'm_unMemorySize',					//
//			  'm_BitMapInfo.bmiHeader.biWidth' and 'm_BitMapInfo.bmiHeader.biHeight' to 0.				//
//																																	//
/////////////////////////////////////////////////////////////////////////////////////////////////////

JCH_Pixel * JCH_PixelMap::DetachMemory( unsigned int *punMemorySize, int *pnWidth, int *pnHeight,
															  int *pnXPelsPerMeter, int *pnYPelsPerMeter )
{
	JCH_Pixel  *pFirstPixelBkp = m_pFirstPixel;

	if ( punMemorySize	!= 0 ) *(punMemorySize)	  = GetMemorySize(); 
	if ( pnWidth			!= 0 ) *(pnWidth)			  = GetWidth(); 
	if ( pnHeight			!= 0 ) *(pnHeight)		  = GetHeight();
	if ( pnXPelsPerMeter != 0 ) *(pnXPelsPerMeter) = GetXPelsPerMeter(); 
	if ( pnYPelsPerMeter != 0 ) *(pnYPelsPerMeter) = GetYPelsPerMeter();

	m_pFirstPixel  = 0;
	m_unMemorySize = 0;				
	m_BitMapInfo.bmiHeader.biWidth  = 0;	
	m_BitMapInfo.bmiHeader.biHeight = 0;				

	return( pFirstPixelBkp );
}


/////////////////////////////////////////////////////////////////////////////////////////////////////
//																																	//
//	Coded by www.adeluc.com   All rights reserved.    Created: 1999-02-06©   Modified: 2004-11-29©	//
//																																	//
//	Purpose:																														//
//																																	//
//	Result: 																														//
//																																	//
//	IMPORTANT: These functions do not make any check for overflow.  Use Safe_Fill(), Safe_FillRGB()	//
//				  or Safe_FillAlpha() if you are not sure of your values.										//
//																																	//
/////////////////////////////////////////////////////////////////////////////////////////////////////

void JCH_PixelMap::Fill( const CRect cRectangle, const JCH_Pixel cpixFillValue ) 
{	
	JCH_Pixel  *ppixCurrentPixel;
	int			nX, nY;

	for( nY = cRectangle.top; nY < cRectangle.bottom; nY++ )
	{
		ppixCurrentPixel = &m_pFirstPixel[((GetHeight() - 1 - nY) * GetWidth()) + cRectangle.left];

		for( nX = (cRectangle.right - cRectangle.left); nX > 0; nX-- )
		{
			*(ppixCurrentPixel++) = cpixFillValue;	
		}
	}
}	


/////////////////////////////////////////////////////////////////////////////////////////////////////
//																																	//
//	Coded by www.adeluc.com   All rights reserved.    Created: 2007-04-22©   Modified: 2007-06-10©	//
//																																	//
//	Purpose:	Blend the specified pixels from 'this' to 'ppixTargetFirstPixel' with also the			//
//				possibility to use an optional source constant alpha value.										//
//																																	//
//	'cbSourceIsPremultiplied':	Indicates if the source RGB components have been already scaled by 	//
//										their corresponding alpha component.											//
//																																	//
//	'cunSourceConstantAlpha': Alpha value that will be applied to the RGBA components of the source //
//									  rectangle before blending it with the target rectangle.					//
//																																	//
//	'cbIgnoreSourceAlphaChannel': When set to TRUE then the source alpha channel values are all		//
//											considered as 0xFF for the calculations.									//
//																																	//
//	Note: When 'cbSourceIsPremultiplied' and  'cbIgnoreSourceAlphaChannel' are both set to TRUE 		//
//			then the resulting image might be darker than expected because the source RGB components	//
//			have been already scaled by some alpha values != 0xFF.  So it is recommended to avoid		//
//			setting those two flags to TRUE at the same time, unless you really want this result.		//
//																																	//
//	IMPORTANT: This function does not make any check for overflow.												//
//				  Use Safe_AlphaBlend() if you are not sure of your values.										//
//																																	//
/////////////////////////////////////////////////////////////////////////////////////////////////////

void JCH_PixelMap::AlphaBlend( const int cnSourcePositionX, const int cnSourcePositionY,
										 const int cnTargetPositionX, const int cnTargetPositionY,
										 const int cnWidth, const int cnHeight, JCH_Pixel *ppixTargetFirstPixel,
										 const int cnTargetTotalWidth, const int cnTargetTotalHeight,
										 const bool cbSourceIsPremultiplied, const unsigned int cunSourceConstantAlpha, 
										 const bool cbIgnoreSourceAlphaChannel ) const
{
	const JCH_Pixel	  *pcpixSourcePixel;
	JCH_Pixel			  *ppixTargetPixel;
	JCH_Pixel				pixSource, pixTarget;
	int						nX, nY;
	unsigned int			unAlpha1, unAlpha2, unAlpha1Factor;

	const unsigned int	cunSmallDeno	 = JCH_PIXEL_CHANNEL_MAX_VAL;
	const unsigned int	cunSmallDenoOn2 = JCH_PIXEL_CHANNEL_HALFMAX_VAL;

//---------------------------------------------------------------------------------------------------

	if ( cunSourceConstantAlpha == JCH_PIXEL_CHANNEL_MIN_VAL )  
	{
		//	Nothing to do.
	}

//---------------------------------------------------------------------------------------------------

	else if ( cbIgnoreSourceAlphaChannel && (cunSourceConstantAlpha == JCH_PIXEL_CHANNEL_MAX_VAL) )  
	{
		for( nY = (cnHeight - 1); nY >= 0; nY-- )
		{
			pcpixSourcePixel = &m_pFirstPixel[((GetHeight() - 1 -	cnSourcePositionY - nY) * GetWidth()) + cnSourcePositionX];
			ppixTargetPixel  = &ppixTargetFirstPixel[((cnTargetTotalHeight - 1 - cnTargetPositionY - nY) * cnTargetTotalWidth) + cnTargetPositionX];

			for( nX = (cnWidth - 1); nX >= 0; nX-- ) *(ppixTargetPixel++) = (*(pcpixSourcePixel++) | 0xFF000000);
		}
	}

//---------------------------------------------------------------------------------------------------

	else if ( !cbIgnoreSourceAlphaChannel && cbSourceIsPremultiplied &&
					(cunSourceConstantAlpha == JCH_PIXEL_CHANNEL_MAX_VAL) )
	{
		for( nY = (cnHeight - 1); nY >= 0; nY-- )
		{
			pcpixSourcePixel = &m_pFirstPixel[((GetHeight() - 1 -	cnSourcePositionY - nY) * GetWidth()) + cnSourcePositionX];
			ppixTargetPixel  = &ppixTargetFirstPixel[((cnTargetTotalHeight - 1 - cnTargetPositionY - nY) * cnTargetTotalWidth) + cnTargetPositionX];

			for( nX = (cnWidth - 1); nX >= 0; nX-- )
			{
				pixSource = *(pcpixSourcePixel++);
				pixTarget = *(ppixTargetPixel);
				unAlpha1  = JCH_PixelGetAlpha( pixSource );
				unAlpha2  = (JCH_PIXEL_CHANNEL_MAX_VAL - unAlpha1);

				*(ppixTargetPixel++) =
					(((unAlpha1 +
					(((JCH_PixelGetAlpha( pixTarget ) * unAlpha2) + cunSmallDenoOn2) / cunSmallDeno)) << 24) |
					 ((JCH_PixelGetRed(   pixSource ) +
					(((JCH_PixelGetRed(   pixTarget ) * unAlpha2) + cunSmallDenoOn2) / cunSmallDeno)) << 16) |
					 ((JCH_PixelGetGreen( pixSource ) +
					(((JCH_PixelGetGreen( pixTarget ) * unAlpha2) + cunSmallDenoOn2) / cunSmallDeno)) <<  8) |
					  (JCH_PixelGetBlue(  pixSource ) +
					(((JCH_PixelGetBlue(  pixTarget ) * unAlpha2) + cunSmallDenoOn2) / cunSmallDeno)));
			}
		}
	}

//---------------------------------------------------------------------------------------------------

	else	//	All other flag combinations are done here.
	{
		for( nY = (cnHeight - 1); nY >= 0; nY-- )
		{
			pcpixSourcePixel = &m_pFirstPixel[((GetHeight() - 1 -	cnSourcePositionY - nY) * GetWidth()) + cnSourcePositionX];
			ppixTargetPixel  = &ppixTargetFirstPixel[((cnTargetTotalHeight - 1 - cnTargetPositionY - nY) * cnTargetTotalWidth) + cnTargetPositionX];

			for( nX = (cnWidth - 1); nX >= 0; nX-- )	//	Here 'nX' value is inversed compared to pointers values.
			{
				pixSource = *(pcpixSourcePixel++);
				pixTarget = *(ppixTargetPixel);

				if ( !cbIgnoreSourceAlphaChannel )
				{
					unAlpha1			= (((JCH_PixelGetAlpha( pixSource ) * cunSourceConstantAlpha) + cunSmallDenoOn2) / cunSmallDeno);
					unAlpha1Factor = (cbSourceIsPremultiplied ? cunSourceConstantAlpha : unAlpha1);
				}
				else unAlpha1 = unAlpha1Factor = cunSourceConstantAlpha;

				unAlpha2 = (JCH_PIXEL_CHANNEL_MAX_VAL - unAlpha1);

				*(ppixTargetPixel++) = 
					(((unAlpha1 + 
				   (((JCH_PixelGetAlpha( pixTarget ) * unAlpha2) + cunSmallDenoOn2) / cunSmallDeno)) << 24) |
				  ((((JCH_PixelGetRed(   pixSource ) * unAlpha1Factor) +
					  (JCH_PixelGetRed(   pixTarget ) * unAlpha2) + cunSmallDenoOn2) / cunSmallDeno) << 16) |
				  ((((JCH_PixelGetGreen( pixSource ) * unAlpha1Factor) +
					  (JCH_PixelGetGreen( pixTarget ) * unAlpha2) + cunSmallDenoOn2) / cunSmallDeno) <<  8) |
				   (((JCH_PixelGetBlue(  pixSource ) * unAlpha1Factor) +
					  (JCH_PixelGetBlue(  pixTarget ) * unAlpha2) + cunSmallDenoOn2) / cunSmallDeno));
			}
		}
	}
}
