#include "stdafx.h"
#include "dibsection.hpp"
#include "dibsurf.hpp"      //lint !e537
#include <stack>
#include <vector>

//*******************************************************************
//
//    DESCRIPTION:Implementation file for CDIBSurface support functions
//
//    AUTHOR: Andrew Farrier
//
//    SourceSafe Version: $Revision: 17 $
//
//******************************************************************

extern int 	g_NumDibSurf;

void CDIBSurface::ClearData (void)
{
	ASSERT (m_DIBData);
	m_DIBData->m_UseMask = false;
	m_DIBData->m_BackBits = NULL;
	m_DIBData->m_SelectedDC = NULL;
	m_DIBData->m_SelectedDCRefCount = 0;
	m_DIBData->m_Mask = NULL;
//	m_DIBData->m_OldBits=NULL;
	m_DIBData->m_Data=NULL;
	m_DIBData->m_UpperLeft=NULL;
	m_DIBData->m_Width = m_DIBData->m_Height = m_DIBData->m_Pitch = m_DIBData->m_BitDepth = 0;
}

void CDIBSurface::HLine( int x1, int x2, int y, COLORREF crColor )
{
	int nWidth = GetWidth();
	int nHeight = GetHeight();

	if( 0 <= x1 && nWidth > x1 &&
		0 <= x2 && nWidth > x2 &&
		0 <= y && nHeight > y )
	{
		int nPixelSize = GetBitDepth() >> 3;
		BYTE *pBits = GetMem( x1, y );

		switch( nPixelSize )
		{
			case 2:		//  16-bit
			{
				WORD wColor = ConvertColorToWord( crColor );
				WORD *pData = (WORD *)pBits;

				while( x1++ <= x2 )
					*pData++ = wColor;
				break;
			}

			case 3:		//  24-bit
			{
				RGBTRIPLE rgbColor = ConvertColorToTriple( crColor );
				RGBTRIPLE *pData = (RGBTRIPLE *)pBits;

				while( x1++ <= x2 )
					*pData++ = rgbColor;

				break;
			}

			case 4:		//  32-bit
			{
				DWORD dwColor = ConvertColorToLong( crColor );
				DWORD *pData = (DWORD *)pBits;

				while( x1++ <= x2 )
					*pData++ = dwColor;
				break;
			}

			default:
				ASSERT( false );
				break;
		}
	}
}

bool CDIBSurface::HighlightFrame (const CRect& inrect,bool up)
{
	int x,y;
	union
	{
		COLORREF color;
		BYTE value[4];
	} color;
	const int delta=60;

	CRect therect (inrect);
	--therect.right;
	--therect.bottom;

	for(x=therect.left+1;x<therect.right;x++)
	{
		color.color = GetColor (x,therect.top);
		if(up)
		{
			color.value[1] = clamp<int> (color.value[1],color.value[1]+delta,255);
			color.value[2] = clamp<int> (color.value[2],color.value[2]+delta,255);
			color.value[0] = clamp<int> (color.value[0],color.value[0]+delta,255);
		}
		else
		{
			color.value[1] = clamp<int> (0,color.value[1]-delta,color.value[1]);
			color.value[2] = clamp<int> (0,color.value[2]-delta,color.value[2]);
			color.value[0] = clamp<int> (0,color.value[0]-delta,color.value[0]);
		}
		SetColor (x,therect.top,color.color);
		
		color.color = GetColor (x,therect.bottom);
		if(up)
		{
			color.value[1] = clamp<int> (0,color.value[1]-delta,color.value[1]);
			color.value[2] = clamp<int> (0,color.value[2]-delta,color.value[2]);
			color.value[0] = clamp<int> (0,color.value[0]-delta,color.value[0]);
		}
		else
		{
			color.value[1] = clamp<int> (color.value[1],color.value[1]+delta,255);
			color.value[2] = clamp<int> (color.value[2],color.value[2]+delta,255);
			color.value[0] = clamp<int> (color.value[0],color.value[0]+delta,255);
		}
		SetColor (x,therect.bottom,color.color);
	}
	for(y=therect.top;y<therect.bottom;y++)
	{
		color.color = GetColor (therect.left,y);
		if(up)
		{
			color.value[1] = clamp<int> (color.value[1],color.value[1]+delta,255);
			color.value[2] = clamp<int> (color.value[2],color.value[2]+delta,255);
			color.value[0] = clamp<int> (color.value[0],color.value[0]+delta,255);
		}
		else
		{
			color.value[1] = clamp<int> (0,color.value[1]-delta,color.value[1]);
			color.value[2] = clamp<int> (0,color.value[2]-delta,color.value[2]);
			color.value[0] = clamp<int> (0,color.value[0]-delta,color.value[0]);
		}
		SetColor (therect.left,y,color.color);
		
		color.color = GetColor (therect.right,y);
		if(up)
		{
			color.value[1] = clamp<int> (0,color.value[1]-delta,color.value[1]);
			color.value[2] = clamp<int> (0,color.value[2]-delta,color.value[2]);
			color.value[0] = clamp<int> (0,color.value[0]-delta,color.value[0]);
		}
		else
		{
			color.value[1] = clamp<int> (color.value[1],color.value[1]+delta,255);
			color.value[2] = clamp<int> (color.value[2],color.value[2]+delta,255);
			color.value[0] = clamp<int> (color.value[0],color.value[0]+delta,255);
		}
		SetColor (therect.right,y,color.color);
	}
	return true;
}

bool CDIBSurface::SetupLoadedBitmap (HBITMAP hbm)
{
	BITMAP              bm;

	ASSERT (m_DIBData);
	Destroy ();

	if(hbm == NULL)
		return false;

	g_NumDibSurf++;

	//
	// get info about the bitmap
	//
	if(GetObject(hbm, sizeof(bm), &bm) == 0)
		return false;

	m_DIBData->m_BackBits = hbm;
	m_DIBData->m_Data = (unsigned char *)bm.bmBits;
	m_DIBData->m_Width = (UINT) bm.bmWidth;
	m_DIBData->m_Height = (UINT) abs(bm.bmHeight);
	m_DIBData->m_BitDepth = bm.bmPlanes * bm.bmBitsPixel;
	m_DIBData->m_Pitch = bm.bmWidthBytes;

// workaround for bug in 95 where 24 images are not quad byte aligned
	OSVERSIONINFO temp;
	temp.dwOSVersionInfoSize = sizeof (temp);
	if(!GetVersionEx (&temp))
		return false;
	if((temp.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS) || (m_DIBData->m_BitDepth != 24))
		m_DIBData->m_Pitch += ((4-(m_DIBData->m_Pitch&0x03))&0x03);

	if(bm.bmHeight > 0)
		m_DIBData->m_Pitch *= -1;

	if(m_DIBData->m_Pitch < 0)		// only the case if we have a bottom-up section
		m_DIBData->m_UpperLeft = (m_DIBData->m_Data+((-1*m_DIBData->m_Pitch)*(((int)m_DIBData->m_Height)-1)));
	else
		m_DIBData->m_UpperLeft = m_DIBData->m_Data;

	// create a DC to use the DIB section in
//	m_DIBData->m_BackDC = CreateCompatibleDC (NULL);
	// select the dib section into the DC from above
//	m_DIBData->m_OldBits = (HBITMAP) SelectObject (m_DIBData->m_BackDC,m_DIBData->m_BackBits);

	return true;
}

//****************************************************************
//
// Function Name: CDIBSurface::CopyBitmap
//
// Description: Will copy hbm to the current DIBSection.  Will perform a
//          stretchblt if needed.  You can pass different coordinates if needed
//          User is responsible for making sure the color tables match
//
// Returns: true if successful
//
//****************************************************************
bool CDIBSurface::CopyBitmap(HBITMAP hbm,int x,int y,int dx,int dy)
{
	HDC                 hdcImage;
	BITMAP              bm;
	HBITMAP temp;
	bool err = false;

	ASSERT (m_DIBData);
	ASSERT (false);

	if(hbm)
	{
		//
		//  select bitmap into a memoryDC so we can use it.
		//

		hdcImage = CreateCompatibleDC(NULL);
		if(!hdcImage)
			OutputDebugString("create compatible dc failed\n");
		temp = (HBITMAP) SelectObject(hdcImage, hbm);

		//
		// get size of the bitmap
		//
		if(GetObject(hbm, sizeof(bm), &bm) == 0)		// get size of bitmap
			return false;
		dx = dx == 0 ? bm.bmWidth  : dx;		// use the passed size, unless zero
		dy = dy == 0 ? bm.bmHeight : dy;

		if(!StretchBlt(0, 0, (int) GetWidth (),(int) GetHeight (), hdcImage, x, y, dx, dy, SRCCOPY))
			err = true;

		SelectObject (hdcImage,temp);
		DeleteDC(hdcImage);
	}
	return err;
}

//****************************************************************
//
// Function Name: CDIBSurface::Create1BitMask
//
// Description: Parse a 1-bit DIBSurface and create a 1-bit mask
//
// Returns: true if successful
//
//****************************************************************
bool CDIBSurface::Create1BitMask (COLORREF colorref)
{
	unsigned char *inmask,*inpic;
	unsigned char *maskline,*picline;
	int count;
	UINT x,y,i;
	int red,green,blue;
	int maskpitch,picpitch;
	int colorindex,value;
	RGBQUAD colors[2];

	ASSERT (m_DIBData);
	ASSERT (m_DIBData->m_Mask);
	if(!m_DIBData->m_Mask)
		return false;

	HDC dc;
	dc = GetDC ();
	if(GetDIBColorTable (dc,0,2,&(colors[0])) == 0)
	{
		ReleaseDC (dc);
		return false;
	}
	ReleaseDC (dc);
	red = colorref & 0xff;
	green = (colorref>>8)&0xff;
	blue = (colorref>>16)&0xff;
	colorindex = -1;
	for(i=0;i<2;i++)
	{
		if(red != colors[i].rgbRed)
			continue;
		if(green != colors[i].rgbGreen)
			continue;
		if(blue != colors[i].rgbBlue)
			continue;
		colorindex = (int) i;
		break;
	}
	if(colorindex == -1)
		return false;

	maskline = m_DIBData->m_Mask->GetMem ();
	maskpitch = m_DIBData->m_Mask->GetPitch ();
	picline = GetMem ();
	picpitch = GetPitch ();
	for(y=0;y<m_DIBData->m_Height;y++)
	{
		inmask = maskline;
		inpic = picline;
		*inmask = 0;
		count = 7;
		for(x=0;x<m_DIBData->m_Width;x++,count--)
		{
			if(count < 0)
			{
				inmask++;
				*inmask = 0;
				count = 7;
				inpic++;
			}
			value = ((*inpic)>>count)&0x01;

			if(value == colorindex)
			{
				(*inmask) += (1<<count);
				(*inpic) &= ~(1<<count);
			}
		}
		maskline += maskpitch;
		picline += picpitch;
	}

	return true;
}
//****************************************************************
//
// Function Name: CDIBSurface::Create4BitMask
//
// Description: Parse a 4-bit DIBSurface and create a 1-bit mask
//
// Returns: true if successful
//
//****************************************************************
bool CDIBSurface::Create4BitMask (COLORREF colorref)
{
	unsigned char *inmask,*inpic;
	unsigned char *maskline,*picline;
	int count;
	UINT x,y,i;
	int red,green,blue;
	int maskpitch,picpitch;
	int colorindex,value;
	RGBQUAD colors[16];

	ASSERT (m_DIBData);
	ASSERT (m_DIBData->m_Mask);
	if(!m_DIBData->m_Mask)
		return false;

	HDC dc;
	dc = GetDC ();
	if(GetDIBColorTable (dc,0,16,&(colors[0])) == 0)
	{
		ReleaseDC (dc);
		return false;
	}
	ReleaseDC (dc);
	red = colorref & 0xff;
	green = (colorref>>8)&0xff;
	blue = (colorref>>16)&0xff;
	colorindex = -1;
	for(i=0;i<16;i++)
	{
		if(red != colors[i].rgbRed)
			continue;
		if(green != colors[i].rgbGreen)
			continue;
		if(blue != colors[i].rgbBlue)
			continue;
		colorindex = (int) i;
		break;
	}
	if(colorindex == -1)
		return false;

	maskline = m_DIBData->m_Mask->GetMem ();
	maskpitch = m_DIBData->m_Mask->GetPitch ();
	picline = GetMem ();
	picpitch = GetPitch ();
	for(y=0;y<m_DIBData->m_Height;y++)
	{
		inmask = maskline;
		inpic = picline;
		*inmask = 0;
		count = 7;
		for(x=0;x<m_DIBData->m_Width;x++,count--)
		{
			if(count < 0)
			{
				inmask++;
				*inmask = 0;
				count = 7;
			}

			if(x&0x01)
				value = ((*inpic)) & 0x0f;
			else
				value	= ((*inpic)>>4) & 0x0f;

			if(value == colorindex)
			{
				(*inmask) += (1<<count);
				if(x&0x01)
					(*inpic) &= 0xf0;
				else
					(*inpic) &= 0x0f;
			}

			if(x&0x01)
				inpic++;
		}
		maskline += maskpitch;
		picline += picpitch;
	}

	return true;
}
//****************************************************************
//
// Function Name: CDIBSurface::Create8BitMask
//
// Description: Parse a 8-bit DIBSurface and create a 1-bit mask
//
// Returns: true if successful
//
//****************************************************************
bool CDIBSurface::Create8BitMask (COLORREF colorref)
{
	unsigned char *inmask,*inpic;
	unsigned char *maskline,*picline;
	int count;
	UINT x,y,i;
	int red,green,blue;
	int maskpitch,picpitch;
	int colorindex,value;
	RGBQUAD colors[256];

	ASSERT (m_DIBData);
	ASSERT (m_DIBData->m_Mask);
	if(!m_DIBData->m_Mask)
		return false;

	HDC dc;
	dc  = GetDC ();
	if(GetDIBColorTable (dc,0,256,&(colors[0])) == 0)
	{
		ReleaseDC (dc);
		return false;
	}
	ReleaseDC (dc);
	red = colorref & 0xff;
	green = (colorref>>8)&0xff;
	blue = (colorref>>16)&0xff;
	colorindex = -1;
	for(i=0;i<256;i++)
	{
		if(red != colors[i].rgbRed)
			continue;
		if(green != colors[i].rgbGreen)
			continue;
		if(blue != colors[i].rgbBlue)
			continue;
		colorindex = (int) i;
		break;
	}
	if(colorindex == -1)
		return false;

	maskline = m_DIBData->m_Mask->GetMem ();
	maskpitch = m_DIBData->m_Mask->GetPitch ();
	picline = GetMem ();
	picpitch = GetPitch ();
	for(y=0;y<m_DIBData->m_Height;y++)
	{
		inmask = maskline;
		inpic = picline;
		*inmask = 0;
		count = 7;
		for(x=0;x<m_DIBData->m_Width;x++,count--)
		{
			if(count < 0)
			{
				inmask++;
				*inmask = 0;
				count = 7;
			}

			value = *inpic;
			if(value == colorindex)
			{
				(*inmask) += (1<<count);
				(*inpic) = 0;
			}
			inpic += 1;
		}
		maskline += maskpitch;
		picline += picpitch;
	}

	return true;
}
//****************************************************************
//
// Function Name: CDIBSurface::Create16BitMask
//
// Description: Parse a 16-bit DIBSurface and create a 1-bit mask
//
// Returns: true if successful
//
//****************************************************************
bool CDIBSurface::Create16BitMask (COLORREF colorref)
{
	unsigned char *inmask,*inpic;
	unsigned char *maskline,*picline;
	int count;
	UINT x,y;
	int maskpitch,picpitch;
	DWORD value,red,green,blue;
	DWORD color;

	ASSERT (m_DIBData);
	ASSERT (m_DIBData->m_Mask);
	if(!m_DIBData->m_Mask)
		return false;

	red = colorref & 0xff;
	green = (colorref>>8)&0xff;
	blue = (colorref>>16)&0xff;
	red >>= 3;
	green >>= 3;
	blue >>= 3;
	color = (red<<10)+(green<<5)+blue;

	maskline = m_DIBData->m_Mask->GetMem ();
	maskpitch = m_DIBData->m_Mask->GetPitch ();
	picline = GetMem ();
	picpitch = GetPitch ();
	for(y=0;y<m_DIBData->m_Height;y++)
	{
		inmask = maskline;
		inpic = picline;
		*inmask = 0;
		count = 7;
		for(x=0;x<m_DIBData->m_Width;x++,count--)
		{
			if(count < 0)
			{
				inmask++;
				*inmask = 0;
				count = 7;
			}

			value = (*(inpic+1)<<8) + *(inpic);

			if(value == color)
			{
				(*inmask) += (1<<count);
				(*inpic) = 0;
				(*(inpic+1)) = 0;
			}
			inpic += 2;
		}
		maskline += maskpitch;
		picline += picpitch;
	}

	return true;
}
//****************************************************************
//
// Function Name: CDIBSurface::Create24BitMask
//
// Description: Parse a 24-bit DIBSurface and create a 1-bit mask
//
// Returns: true if successful
//
//****************************************************************
bool CDIBSurface::Create24BitMask (COLORREF colorref)
{
	unsigned char *inmask,*inpic;
	unsigned char *maskline,*picline;
	int count;
	UINT x,y;
	int maskpitch,picpitch;
	COLORREF curcolor;

	ASSERT (m_DIBData);
	ASSERT (m_DIBData->m_Mask);
	if(!m_DIBData->m_Mask)
		return false;

	maskline = m_DIBData->m_Mask->GetMem ();
	maskpitch = m_DIBData->m_Mask->GetPitch ();
	picline = GetMem ();
	picpitch = GetPitch ();
	for(y=0;y<m_DIBData->m_Height;y++)
	{
		inmask = maskline;
		inpic = picline;
		*inmask = 0;
		count = 7;
		for(x=0;x<m_DIBData->m_Width;x++,count--)
		{
			if(count < 0)
			{
				inmask++;
				*inmask = 0;
				count = 7;
			}

			curcolor = ((*inpic)<<16) + ((*(inpic+1))<<8) + ((*(inpic+2)));
			if(curcolor == colorref)
			{
				(*inmask) += (1<<count);
				(*inpic) = 0;
				(*(inpic+1)) = 0;
				(*(inpic+2)) = 0;
			}
			inpic += 3;
		}
		maskline += maskpitch;
		picline += picpitch;
	}


	return true;
}

int CDIBSurface::FloodFill24 (const CPoint& start,const COLORREF color)
{
	COLORREF srccolor,intcolor;
	FloodData temp (0,0);
	std::stack<FloodData> data;
	int count=0;
	int pitch;
	int red,green,blue;
	unsigned char *mem;

	ASSERT (m_DIBData);
	intcolor = color;
	srccolor = GetColor (start.x,start.y);
	if(srccolor == intcolor)
		return 0;
	mem = GetMem () + GetLocation (start.x,start.y);
	pitch = GetPitch ();
	data.push (FloodData (mem,start));
	while(!data.empty ())
	{
		temp = data.top ();
		data.pop ();

		intcolor = ((*temp.mem)<<16)+ ((*(temp.mem+1))<<8) + (*(temp.mem+2));
		
		if(intcolor==srccolor)
		{
			red = color&0xff;
			green = (color>>8) & 0xff;
			blue = (color>>16) & 0xff;
	
			mem = temp.mem;
			(*mem) = blue;
			mem++;
			(*mem) = green;
			mem++;
			(*mem) = red;
			mem++;

			count++;
			if(temp.where.y != 0)
				data.push (FloodData (temp.mem-pitch,CPoint(temp.where.x,temp.where.y-1)));
			if(temp.where.y != (m_DIBData->m_Height-1))
				data.push (FloodData (temp.mem+pitch,CPoint(temp.where.x,temp.where.y+1)));
			if(temp.where.x != 0)
				data.push (FloodData (temp.mem-3,CPoint(temp.where.x-1,temp.where.y)));
			if(temp.where.x != (m_DIBData->m_Width-1))
				data.push (FloodData (temp.mem+3,CPoint(temp.where.x+1,temp.where.y)));
		}
	}
	return count;
}

bool CDIBSurface::AlphaBlt16 (const int x,const int y,const int width,const int height,const CDIBSurface *srcdib,const CDIBSurface *alphadib,const int srcx,const int srcy,const int alphasrcx,const int alphasrcy)
{
	float	red,green,blue;
	float sred,sgreen,sblue;
	float dred,dgreen,dblue;
	int ired,igreen,iblue;
	float avalue;
	int cx,cy;
	int iaval;

	int dpitch,spitch,apitch;
	WORD *dest,*dline;
	const WORD *src,*sline;
	const unsigned char *alpha,*aline;

	dline = (WORD *) (GetMem ()+GetLocation (x,y));
	sline = (const WORD *) (srcdib->GetMem () + srcdib->GetLocation (srcx,srcy));
	aline = (alphadib->GetMem () + alphadib->GetLocation (alphasrcx,alphasrcy));
	dpitch = GetPitch ();
	dpitch >>= 1;
	spitch = srcdib->GetPitch ();
	spitch >>=1;
	apitch = alphadib->GetPitch ();



	for(cy = 0;cy < height;cy++)
	{
		dest = dline;
		src = sline;
		alpha = aline;
		for(cx = 0;cx < width;cx++)
		{
			iaval = *alpha;
			if(iaval == 0)
			{
					
			}
			else if(iaval == 255)
			{
				*dest = *src;
			}
			else
			{
				avalue = iaval;
	
				dblue = (*dest)&0x001f;
				dgreen = ((*dest)>>5)&0x001f;
				dred = ((*dest)>>10)&0x001f;
				
				sblue = (*src)&0x001f;
				sgreen = ((*src)>>5)&0x001f;
				sred = ((*src)>>10)&0x001f;
				avalue /= 255.0;
	
				red = (sred * avalue) + (dred*(1.0-avalue));
				green = (sgreen * avalue) + (dgreen*(1.0-avalue));
				blue = (sblue * avalue) + (dblue*(1.0-avalue));
				ired = red;
				igreen = green;
				iblue = blue;

				*dest = (ired<<10) + (igreen<<5) + iblue;
			}

			dest++;
			src++;
			alpha++;
		}
		dline += dpitch;
		sline += spitch;
		aline += apitch;
	}

	return true;
}

bool CDIBSurface::AlphaBlt24 (const int x,const int y,const int width,const int height,const CDIBSurface *srcdib,const CDIBSurface *alphadib,const int srcx,const int srcy,const int alphasrcx,const int alphasrcy)
{
	float	red,green,blue;
	float sred,sgreen,sblue;
	float dred,dgreen,dblue;
	float avalue;
	int cx,cy;
	int iaval;

	int dpitch,spitch,apitch;
	unsigned char *dest,*dline;
	const unsigned char *src,*sline;
	const unsigned char *alpha,*aline;

	dline = GetMem ()+GetLocation (x,y);
	sline = srcdib->GetMem () + srcdib->GetLocation (srcx,srcy);
	aline = alphadib->GetMem () + alphadib->GetLocation (alphasrcx,alphasrcy);
	dpitch = GetPitch ();
	spitch = srcdib->GetPitch ();
	apitch = alphadib->GetPitch ();

	for(cy = 0;cy < height;cy++)
	{
		dest = dline;
		src = sline;
		alpha = aline;
		for(cx = 0;cx < width;cx++)
		{
			iaval = *alpha;
			if(iaval == 0)
			{
					
			}
			else if(iaval == 255)
			{
				*dest = *src;
				*(dest+1) = *(src+1);
				*(dest+2) = *(src+2);
			}
			else
			{
				avalue = iaval;
	
				dblue = *dest;
				dgreen = *(dest+1);
				dred = *(dest+2);
				sblue = *src;
				sgreen = *(src+1);
				sred = *(src+2);
				avalue /= 255.0;
	
				red = (sred * avalue) + (dred*(1.0-avalue));
				green = (sgreen * avalue) + (dgreen*(1.0-avalue));
				blue = (sblue * avalue) + (dblue*(1.0-avalue));
				*dest = blue;
				*(dest+1) = green;
				*(dest+2) = red;
			}

			dest += 3;
			src += 3;
			alpha++;
		}
		dline += dpitch;
		sline += spitch;
		aline += apitch;
	}

	return true;
}
	
bool CDIBSurface::Convert24to16 (void)
{
	ASSERT (m_DIBData);
	CDIBSurface surf;
	
	if(GetBitDepth () == 16)
		return true;
	ASSERT (GetBitDepth () == 24);
	if(GetBitDepth () != 24)
		return false;
	if (!surf.Create (GetWidth (),GetHeight (),16))
		return false;

	
	BYTE *pDestMem = surf.GetMem();
	ASSERT (pDestMem);
	WORD *pDestLine = (WORD *) pDestMem;
	BYTE *pSrcMem = GetMem ();
	ASSERT (pSrcMem);
	BYTE *pSrcLine = pSrcMem;

	int SrcPitch = GetPitch ();
	int DestPitch = surf.GetPitch ();

	DWORD red,green,blue;
	DWORD rederror=0,greenerror=0,blueerror=0;

	for (int y = 0; y < GetHeight(); ++y)
	{
		rederror = greenerror = blueerror = 0;

		for (int x=0;x<GetWidth ();x++)
		{
			
			blue = *pSrcLine;
			green = *(pSrcLine+1);
			red = *(pSrcLine+2);
			pSrcLine += 3;

			rederror +=	(red & 0x07);
			greenerror += (green & 0x07);
			blueerror += (blue & 0x07);

			if (rederror > 8)
			{
				red += 8;
				if (red > 255)
					red = 255;
				rederror -= 8;
			}
			if (greenerror > 8)
			{
				green += 8;
				if (green > 255)
					green = 255;
				greenerror -= 8;
			}
			if (blueerror > 8)
			{
				blue += 8;
				if (blue > 255)
					blue = 255;
				blueerror -= 8;
			}
			
			red >>= 3;
			green >>= 3;
			blue >>= 3;

			*pDestLine++ = (red << 10) | (green << 5) | (blue);

		}
		pDestMem += DestPitch;
		pSrcMem += SrcPitch;
		pDestLine = (WORD *) pDestMem;
		pSrcLine = pSrcMem;
	}
	if(m_DIBData->m_Mask)
	{
		if(!surf.CalcMask ())
			return false;
		HDC dc1,dc2;

		dc1 = surf.m_DIBData->m_Mask->GetDC ();
		dc2 = m_DIBData->m_Mask->GetDC ();
		BitBlt (dc1,0,0,m_DIBData->m_Width,m_DIBData->m_Height,dc2,0,0,SRCCOPY);
		surf.m_DIBData->m_Mask->ReleaseDC (dc1);
		m_DIBData->m_Mask->ReleaseDC (dc2);
	}
	(*surf.m_RefCount)++;
	Destroy ();
	memcpy (m_DIBData,surf.m_DIBData,sizeof (DIBData));
	delete surf.m_DIBData;
	delete surf.m_RefCount;
	surf.m_DIBData = NULL;
	surf.m_RefCount = NULL;
	return true;
}


namespace
{
inline BYTE GetIntensity (COLORREF c)
{
	int r = GetRValue (c);
	int g = GetGValue (c);
	int b = GetBValue (c);
	return (299 * r + 587 * g + 114 * b) / 1000;
}
}


bool CDIBSurface::ConvertToGray (void)
{
	ASSERT (m_DIBData);
	CDIBSurface surf;
	
	if(GetBitDepth () == 8)
		return true;

	if (!surf.Create (GetWidth (),GetHeight (),8))
		return false;

	BYTE *pDestMem = surf.GetMem();
	ASSERT (pDestMem);
	BYTE *pDestLine = pDestMem;
	int DestPitch = surf.GetPitch ();

	for (int y = 0; y < GetHeight(); ++y)
	{
		for (int x=0;x<GetWidth ();x++)
		{
			COLORREF color = GetColor (x, y);
			*pDestLine++ = GetIntensity (color);
		}
		pDestMem += DestPitch;
		pDestLine = pDestMem;
	}
	if(m_DIBData->m_Mask)
	{
		if(!surf.CalcMask ())
			return false;
		HDC dc1,dc2;

		dc1 = surf.m_DIBData->m_Mask->GetDC ();
		dc2 = m_DIBData->m_Mask->GetDC ();
		BitBlt (dc1,0,0,m_DIBData->m_Width,m_DIBData->m_Height,dc2,0,0,SRCCOPY);
		surf.m_DIBData->m_Mask->ReleaseDC (dc1);
		m_DIBData->m_Mask->ReleaseDC (dc2);
	}
	(*surf.m_RefCount)++;
	Destroy ();
	memcpy (m_DIBData,surf.m_DIBData,sizeof (DIBData));
	delete surf.m_DIBData;
	delete surf.m_RefCount;
	surf.m_DIBData = NULL;
	surf.m_RefCount = NULL;
	return true;
}


bool CDIBSurface::ColorBlt16 (const int x,const int y,const int width,const int height,const CDIBSurface* srcdib,const int srcx,const int srcy,const COLORREF color,bool addred,bool addgreen,bool addblue,CDIBSurface *altmask)
{
	int red,green,blue;
	int reddiff,greendiff,bluediff;
	int cx,cy;
	int mindex;
	CDIBSurface *maskdib = NULL;

	int dpitch,spitch,mpitch;
	WORD *dest;
	unsigned char *dline;
	unsigned char *mask,*mline;
	const WORD *src;
	const unsigned char *sline;

	reddiff = (color & 0xff);
	greendiff = ((color>>8) & 0xff);
	bluediff = ((color>>16) & 0xff);
	
	if(!addred)
		reddiff *= -1;
	if(!addgreen)
		greendiff *= -1;
	if(!addblue)
		bluediff *= -1;

	if(altmask)
		maskdib = altmask;
	else if (srcdib->UseMask())
		maskdib = srcdib->m_DIBData->m_Mask;

	dline = GetMem ()+GetLocation (x,y);
	sline = srcdib->GetMem () + srcdib->GetLocation (srcx,srcy);
	if(maskdib)
	{
		mline = maskdib->GetMem () + maskdib->GetLocation (srcx,srcy);
		mpitch = maskdib->GetPitch ();
	}
	dpitch = GetPitch ();
	spitch = srcdib->GetPitch ();

	if(maskdib)
	{
#if 1
		/* faster version */
		int red_map[32];
		int green_map[32];
		int blue_map[32];

		int i;
		for (i = 0; i < 32; i += 1) {
			red_map[i] = clamp<int> (0,reddiff + i,31);
			green_map[i] = clamp<int> (0,greendiff + i,31);
			blue_map[i] = clamp<int> (0,bluediff + i,31);
		}

		for(cy = 0;cy < height;cy++)
		{
			dest = (WORD *) dline;
			src = (WORD *) sline;
			mask = mline;
			mindex = 7-(srcx & 0x07);
			for(cx = 0;cx < width;cx++)
			{
				if(mindex < 0)
				{
					mindex = 7;
					mask++;
				}
				if(!((*mask) & (1<<mindex)))
				{
					blue = blue_map[((*src)&0x001f)];
					green = green_map[((*src)>>5)&0x001f];
					red = red_map[((*src)>>10)&0x001f];

					*dest = (red << 10) | (green << 5) | (blue);
				}
				mindex--;
				dest++;
				src++ ;
			}
			dline += dpitch;
			sline += spitch;
			mline += mpitch;
		}
#else
		for(cy = 0;cy < height;cy++)
		{
			dest = (WORD *) dline;
			src = (WORD *) sline;
			mask = mline;
			mindex = 7-(srcx & 0x07);
			for(cx = 0;cx < width;cx++)
			{
				if(mindex < 0)
				{
					mindex = 7;
					mask++;
				}
				if(!((*mask) & (1<<mindex)))
				{
					blue = clamp<int> (0,((*src)&0x001f)+bluediff,31);
					green = clamp<int> (0,(((*src)>>5)&0x001f)+greendiff,31);
					red = clamp<int> (0,(((*src)>>10)&0x001f)+reddiff,31);

					*dest = (red << 10) | (green << 5) | (blue);
				}
				mindex--;
				dest++;
				src++ ;
			}
			dline += dpitch;
			sline += spitch;
			mline += mpitch;
		}
#endif
	}
	else
	{
		for(cy = 0;cy < height;cy++)
		{
			dest = (WORD *) dline;
			src = (WORD *) sline;
			for(cx = 0;cx < width;cx++)
			{
				blue = clamp<int> (0,((*src)&0x001f)+bluediff,31);
				green = clamp<int> (0,(((*src)>>5)&0x001f)+greendiff,31);
				red = clamp<int> (0,(((*src)>>10)&0x001f)+reddiff,31);

				*dest = (red << 10) | (green << 5) | (blue);
	
				dest++;
				src++;
			}
			dline += dpitch;
			sline += spitch;
		}
	}

	return true;
}

bool CDIBSurface::ColorBlt24 (const int x,const int y,const int width,const int height,const CDIBSurface* srcdib,const int srcx,const int srcy,const COLORREF color,bool addred,bool addgreen,bool addblue,CDIBSurface *altmask)
{
	int reddiff,greendiff,bluediff;
	int cx,cy;
	int mindex;
	CDIBSurface *maskdib = NULL;

	int dpitch,spitch,mpitch;
	unsigned char *dest,*dline;
	unsigned char *mask,*mline;
	const unsigned char *src,*sline;

	reddiff = (color & 0xff);
	greendiff = ((color>>8) & 0xff);
	bluediff = ((color>>16) & 0xff);
	
	if(!addred)
		reddiff *= -1;
	if(!addgreen)
		greendiff *= -1;
	if(!addblue)
		bluediff *= -1;

	if(altmask)
		maskdib = altmask;
	else if (srcdib->UseMask())
		maskdib = srcdib->m_DIBData->m_Mask;

	dline = GetMem ()+GetLocation (x,y);
	sline = srcdib->GetMem () + srcdib->GetLocation (srcx,srcy);
	if(maskdib)
	{
		mline = maskdib->GetMem () + maskdib->GetLocation (srcx,srcy);
		mpitch = maskdib->GetPitch ();
	}
	dpitch = GetPitch ();
	spitch = srcdib->GetPitch ();

	if(maskdib)
	{
		for(cy = 0;cy < height;cy++)
		{
			dest = dline;
			src = sline;
			mask = mline;
			mindex = 7-(srcx & 0x07);
			for(cx = 0;cx < width;cx++)
			{
				if(mindex < 0)
				{
					mindex = 7;
					mask++;
				}
				if(!((*mask) & (1<<mindex)))
				{
					*dest = clamp<int> (0,(*src)+bluediff,255);
					*(dest+1) = clamp<int> (0,(*src+1)+greendiff,255);
					*(dest+2) = clamp<int> (0,(*src+2)+reddiff,255);
				}
				mindex--;
				dest += 3;
				src += 3;
			}
			dline += dpitch;
			sline += spitch;
			mline += mpitch;
		}
	}
	else
	{
		for(cy = 0;cy < height;cy++)
		{
			dest = dline;
			src = sline;
			for(cx = 0;cx < width;cx++)
			{
				*dest = clamp<int> (0,(*src)+bluediff,255);
				*(dest+1) = clamp<int> (0,(*src+1)+greendiff,255);;
				*(dest+2) = clamp<int> (0,(*src+2)+reddiff,255);;
	
				dest += 3;
				src += 3;
			}
			dline += dpitch;
			sline += spitch;
		}
	}

	return true;
}
bool CDIBSurface::GrayBlt16 (const int x,const int y,const int width,const int height,const CDIBSurface* srcdib,const int srcx,const int srcy,CDIBSurface *altmask)
{
	int red,green,blue;
	int cx,cy;
	int mindex;
	CDIBSurface *maskdib = NULL;

	int dpitch,spitch,mpitch;
	WORD *dest;
	unsigned char *dline;
	unsigned char *mask,*mline;
	const WORD *src;
	const unsigned char *sline;

	if(altmask)
		maskdib = altmask;
	else if (srcdib->UseMask())
		maskdib = srcdib->m_DIBData->m_Mask;

	dline = GetMem ()+GetLocation (x,y);
	sline = srcdib->GetMem () + srcdib->GetLocation (srcx,srcy);
	if(maskdib)
	{
		mline = maskdib->GetMem () + maskdib->GetLocation (srcx,srcy);
		mpitch = maskdib->GetPitch ();
	}
	dpitch = GetPitch ();
	spitch = srcdib->GetPitch ();

	if(maskdib)
	{
		for(cy = 0;cy < height;cy++)
		{
			dest = (WORD *) dline;
			src = (WORD *) sline;
			mask = mline;
			mindex = 7-(srcx & 0x07);
			for(cx = 0;cx < width;cx++)
			{
				if(mindex < 0)
				{
					mindex = 7;
					mask++;
				}
				if(!((*mask) & (1<<mindex)))
				{
					blue = clamp<int> (0,(((int)(*src)&0x001f) * 86) >> 10,31); // blue
					green = clamp<int> (0,((((int)(*src)>>5)&0x001f) * 273) >> 10,31); // green 
					red = clamp<int> (0,((((int)(*src)>>10)&0x001f) * 156) >> 10,31); // red

					*dest = ((red+green+blue) << 10) | ((red+green+blue) << 5) | (red+green+blue);
				}
				mindex--;
				dest++;
				src++ ;
			}
			dline += dpitch;
			sline += spitch;
			mline += mpitch;
		}
	}
	else
	{
#ifdef USE_DITHERING
		CFloydSteinberg<CDitherIntensity> dither;
		if (!dither.Create (width, height))
		{
			ASSERT (0);
			return false;
		}

		dither.Begin();
#endif

		for(cy = 0;cy < height;cy++)
		{
#ifdef USE_DITHERING
			dither.BeginScanLine();
#endif
			dest = (WORD *) dline;
			src = (WORD *) sline;
			for(cx = 0;cx < width;cx++)
			{
				const int brightness = 100;
				const int rmult = (428 * brightness / 100);
				const int gmult = (1008 * brightness / 100);
				const int bmult = (612 * brightness / 100);

				red = rmult * (((*src)>>10)&0x001f);
				green = gmult * (((*src)>>5)&0x001f);
				blue = bmult * ((*src)&0x001f);

				int intensity = (red + green + blue) >> 8;
				ASSERT (intensity >= 0);
				ASSERT (intensity < 256);

#ifdef USE_DITHERING
				CDitherIntensity dithintensity (intensity);
				dither.Dither (dithintensity);
				dithintensity.i &= (0x1f << 3);
				dither.Achieved (dithintensity);
				intensity = dithintensity.i;
#endif
				intensity >>= 3;	// convert to 5 bits
				*dest = (intensity << 10) | (intensity << 5) | intensity;
	
				dest++;
				src++;
			}
			dline += dpitch;
			sline += spitch;
		}

#ifdef USE_DITHERING
		dither.End();
#endif
	}

	return true;
}

bool CDIBSurface::GrayBlt24 (const int x,const int y,const int width,const int height,const CDIBSurface* srcdib,const int srcx,const int srcy,CDIBSurface *altmask)
{
	int cx,cy;
	int mindex;
	CDIBSurface *maskdib = NULL;
	int red,green,blue,color;

	int dpitch,spitch,mpitch;
	unsigned char *dest,*dline;
	unsigned char *mask,*mline;
	const unsigned char *src,*sline;

	if(altmask)
		maskdib = altmask;
	else if (srcdib->UseMask())
		maskdib = srcdib->m_DIBData->m_Mask;

	dline = GetMem ()+GetLocation (x,y);
	sline = srcdib->GetMem () + srcdib->GetLocation (srcx,srcy);
	if(maskdib)
	{
		mline = maskdib->GetMem () + maskdib->GetLocation (srcx,srcy);
		mpitch = maskdib->GetPitch ();
	}
	dpitch = GetPitch ();
	spitch = srcdib->GetPitch ();

	if(maskdib)
	{
		for(cy = 0;cy < height;cy++)
		{
			dest = dline;
			src = sline;
			mask = mline;
			mindex = 7-(srcx & 0x07);
			for(cx = 0;cx < width;cx++)
			{
				if(mindex < 0)
				{
					mindex = 7;
					mask++;
				}
				if(!((*mask) & (1<<mindex)))
				{
					blue = clamp<int> (0,(*src) * 64.0/255.0,255); // blue
					green = clamp<int> (0,(*src+1) * 204.0/255.0,255); // green 
					red = clamp<int> (0,(*src+2) * 117.0/255.0,255); // red
					color = (red + green + blue) / 3;
					*dest = color;
					*(dest+1) = color;
					*(dest+2) = color;
				}
				mindex--;
				dest += 3;
				src += 3;
			}
			dline += dpitch;
			sline += spitch;
			mline += mpitch;
		}
	}
	else
	{
		for(cy = 0;cy < height;cy++)
		{
			dest = dline;
			src = sline;
			for(cx = 0;cx < width;cx++)
			{
				blue = clamp<int> (0,(*src) * 64.0/255.0, 255); // blue
				green = clamp<int> (0,(*src+1) * 204.0/255.0,255); // green
				red = clamp<int> (0,(*src+2) * 117.0/255.0,255); // red
				color = (red + green + blue) / 3;
				*dest = color;
				*(dest+1) = color;
				*(dest+2) = color;
	
				dest += 3;
				src += 3;
			}
			dline += dpitch;
			sline += spitch;
		}
	}

	return true;
}

bool CDIBSurface::MultBlt16 (const int x,const int y,const int width,const int height,const CDIBSurface* srcdib,const int srcx,const int srcy,const float red,const float green,const float blue,CDIBSurface *altmask)
{
	int dred,dgreen,dblue;
	int cx,cy;
	int mindex;
	CDIBSurface *maskdib = NULL;

	int dpitch,spitch,mpitch;
	WORD *dest;
	unsigned char *dline;
	unsigned char *mask,*mline;
	const WORD *src;
	const unsigned char *sline;

	if(altmask)
		maskdib = altmask;
	else if (srcdib->UseMask())
		maskdib = srcdib->m_DIBData->m_Mask;

	dline = GetMem ()+GetLocation (x,y);
	sline = srcdib->GetMem () + srcdib->GetLocation (srcx,srcy);
	if(maskdib)
	{
		mline = maskdib->GetMem () + maskdib->GetLocation (srcx,srcy);
		mpitch = maskdib->GetPitch ();
	}
	dpitch = GetPitch ();
	spitch = srcdib->GetPitch ();

	if(maskdib)
	{
#if 1
		/* faster version */
		int red_map[32];
		int green_map[32];
		int blue_map[32];

		int i;
		for (i = 0; i < 32; i += 1) {
			red_map[i] = clamp<int> (0,red * i,31);
			green_map[i] = clamp<int> (0,green * i,31);
			blue_map[i] = clamp<int> (0,blue * i,31);
		}

		for(cy = 0;cy < height;cy++)
		{
			dest = (WORD *) dline;
			src = (WORD *) sline;
			mask = mline;
			mindex = 7-(srcx & 0x07);
			for(cx = 0;cx < width;cx++)
			{
				if(mindex < 0)
				{
					mindex = 7;
					mask++;
				}
				if(!((*mask) & (1<<mindex)))
				{
					dblue = blue_map[((*src)&0x001f)];
					dgreen = green_map[((*src)>>5)&0x001f];
					dred = red_map[((*src)>>10)&0x001f];

					*dest = (dred << 10) | (dgreen << 5) | (dblue);
				}
				mindex--;
				dest++;
				src++ ;
			}
			dline += dpitch;
			sline += spitch;
			mline += mpitch;
		}
#else
		for(cy = 0;cy < height;cy++)
		{
			dest = (WORD *) dline;
			src = (WORD *) sline;
			mask = mline;
			mindex = 7-(srcx & 0x07);
			for(cx = 0;cx < width;cx++)
			{
				if(mindex < 0)
				{
					mindex = 7;
					mask++;
				}
				if(!((*mask) & (1<<mindex)))
				{
					dblue = clamp<int> (0,((*src)&0x001f)*blue,31);
					dgreen = clamp<int> (0,(((*src)>>5)&0x001f)*green,31);
					dred = clamp<int> (0,(((*src)>>10)&0x001f)*red,31);

					*dest = (dred << 10) | (dgreen << 5) | (dblue);
				}
				mindex--;
				dest++;
				src++ ;
			}
			dline += dpitch;
			sline += spitch;
			mline += mpitch;
		}
#endif
	}
	else
	{
		for(cy = 0;cy < height;cy++)
		{
			dest = (WORD *) dline;
			src = (WORD *) sline;
			for(cx = 0;cx < width;cx++)
			{
				dblue = clamp<int> (0,((*src)&0x001f)*blue,31);
				dgreen = clamp<int> (0,(((*src)>>5)&0x001f)*green,31);
				dred = clamp<int> (0,(((*src)>>10)&0x001f)*red,31);

				*dest = (dred << 10) | (dgreen << 5) | (dblue);
	
				dest++;
				src++;
			}
			dline += dpitch;
			sline += spitch;
		}
	}

	return true;
}

bool CDIBSurface::MultBlt24 (const int x,const int y,const int width,const int height,const CDIBSurface* srcdib,const int srcx,const int srcy,const float red,const float green,const float blue,CDIBSurface *altmask)
{
	int cx,cy;
	int mindex;
	CDIBSurface *maskdib = NULL;

	int dpitch,spitch,mpitch;
	unsigned char *dest,*dline;
	unsigned char *mask,*mline;
	const unsigned char *src,*sline;

	if(altmask)
		maskdib = altmask;
	else if (srcdib->UseMask())
		maskdib = srcdib->m_DIBData->m_Mask;

	dline = GetMem ()+GetLocation (x,y);
	sline = srcdib->GetMem () + srcdib->GetLocation (srcx,srcy);
	if(maskdib)
	{
		mline = maskdib->GetMem () + maskdib->GetLocation (srcx,srcy);
		mpitch = maskdib->GetPitch ();
	}
	dpitch = GetPitch ();
	spitch = srcdib->GetPitch ();

	if(maskdib)
	{
		for(cy = 0;cy < height;cy++)
		{
			dest = dline;
			src = sline;
			mask = mline;
			mindex = 7-(srcx & 0x07);
			for(cx = 0;cx < width;cx++)
			{
				if(mindex < 0)
				{
					mindex = 7;
					mask++;
				}
				if(!((*mask) & (1<<mindex)))
				{
					*dest = clamp<int> (0,(*src)*blue,255);
					*(dest+1) = clamp<int> (0,(*src+1)*green,255);
					*(dest+2) = clamp<int> (0,(*src+2)*red,255);
				}
				mindex--;
				dest += 3;
				src += 3;
			}
			dline += dpitch;
			sline += spitch;
			mline += mpitch;
		}
	}
	else
	{
		for(cy = 0;cy < height;cy++)
		{
			dest = dline;
			src = sline;
			for(cx = 0;cx < width;cx++)
			{
				*dest = clamp<int> (0,(*src)*blue,255);
				*(dest+1) = clamp<int> (0,(*src+1)*green,255);;
				*(dest+2) = clamp<int> (0,(*src+2)*red,255);;
	
				dest += 3;
				src += 3;
			}
			dline += dpitch;
			sline += spitch;
		}
	}

	return true;
}

