Click here to Skip to main content
Click here to Skip to main content
Articles » Multimedia » GDI+ » General » Downloads
 
Add your own
alternative version

Image Processing for Dummies with C# and GDI+ Part 6 - The HSL color space

, 28 Jun 2004 CPOL
A discussion of the HSL color space, including code for a color picker and image filters
// XTFilters.cpp: implementation of the CXTFilters class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "CGBitmap.h"

#include <math.h>
#include <vector>
#include <deque>
#include <algorithm>
#include <numeric>
#include <time.h>

#include "CGFilters.h"
#include "assert.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

#define mid(a,b,c) ((((a>b) ? a : b)>c) ? c : max(a,b))

const double RadToDeg = 180/3.1415;

const short CGFilters::FILTER_NONE = 0;
const short CGFilters::FILTER_BILINEAR = 1;

const short CGFilters::EDGE_DETECT_QUICK		= 0;
const short CGFilters::EDGE_DETECT_KIRSH		= 1;
const short CGFilters::EDGE_DETECT_PREWITT		= 2;
const short CGFilters::EDGE_DETECT_SOBEL		= 3;
const short CGFilters::EDGE_DETECT_DIFFERENCE	= 4;
const short CGFilters::EDGE_DETECT_HOMOGENITY	= 5;
const short CGFilters::EDGE_DETECT_HORZ			= 6;
const short CGFilters::EDGE_DETECT_VERT			= 7;

const short CGFilters::HIST_RED		= 0;
const short CGFilters::HIST_GREEN	= 1;
const short CGFilters::HIST_BLUE	= 2;
const short CGFilters::HIST_ALL		= 3;

bool CGFilters::m_bInit = false;
double CGFilters::m_nSin[360];
double CGFilters::m_nCos[360];

void CGFilters::Swirl(CGBitmap * pBmp, double fDegree, short smoothing)
{
//	if (false == m_bInit) Init();

	int nWidth = pBmp->GetWidth(), nHeight = pBmp->GetHeight();

	POINT ** ppPoint = new POINT * [nWidth];
	FLOATPOINT ** ppFPoint = new FLOATPOINT * [nWidth];

	for (int i = 0; i < nWidth; ++i)
	{
		ppFPoint[i] = new FLOATPOINT[nHeight];
		ppPoint[i] = new POINT[nHeight];
	}

	POINT mid;
	mid.x = nWidth/2;
	mid.y = nHeight/2;

	POINT pt;
	pt.x = pt.y = 0;
	double theta, radius;
	double newX, newY;

	for (int x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
		{
			int trueX = x - mid.x;
			int trueY = y - mid.y;
			theta = atan2((trueY),(trueX));

			radius = sqrt(trueX*trueX + trueY*trueY);

//			newX = mid.x + (int)(radius * m_nCos[int(theta * RadToDeg)*10]);// + fDegree * radius)]);
			newX = mid.x + (radius * cos(theta + fDegree * radius));
			if (newX > 0 && newX < nWidth)
				ppPoint[x][y].x = ppFPoint[x][y]. x = newX;
			else
				ppPoint[x][y].x = ppFPoint[x][y].x = x;

//			newY = mid.y + (int)(radius * m_nSin[int(theta * RadToDeg)*10]);// + fDegree * radius)]);
			newY = mid.y + (radius * sin(theta + fDegree * radius));
			if (newY > 0 && newY < nHeight)
				ppPoint[x][y].y = ppFPoint[x][y].y = newY;
			else
				ppPoint[x][y].y = ppFPoint[x][y].y = y;
		}

	switch(smoothing)
	{
	case FILTER_NONE:
		CGFilters::OffsetFilterAbs(pBmp, ppPoint);		
		break;
	case FILTER_BILINEAR:
		CGFilters::OffsetFilterAntiAlias(pBmp, ppFPoint);		
		break;
	default:
		// Means that an inappropriate value was passed for smoothing)
		assert(false);
	}

	for (int j = 0; j < pBmp->GetWidth(); ++j)
	{
		delete [] ppPoint[j];
		delete [] ppFPoint[j];
	}

	delete [] ppPoint;
	delete [] ppFPoint;
}

void CGFilters::Sphere(CGBitmap * pBmp, short smoothing)
{
	int nWidth = pBmp->GetWidth(), nHeight = pBmp->GetHeight();

	POINT ** ppPoint = new POINT * [nWidth];
	FLOATPOINT ** ppFPoint = new FLOATPOINT * [nWidth];

	for (int i = 0; i < nWidth; ++i)
	{
		ppFPoint[i] = new FLOATPOINT[nHeight];
		ppPoint[i] = new POINT[nHeight];
	}

	POINT mid;
	mid.x = nWidth/2;
	mid.y = nHeight/2;

	POINT pt;
	pt.x = pt.y = 0;
	double theta, radius;
	double newX, newY;

	for (int x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
		{
			int trueX = x - mid.x;
			int trueY = y - mid.y;
			theta = atan2((trueY),(trueX));

			radius = sqrt(trueX*trueX + trueY*trueY);

			double newRadius = radius * radius/(max(mid.x, mid.y));

			newX = mid.x + (newRadius * cos(theta));

			if (newX > 0 && newX < nWidth)
				ppPoint[x][y].x = ppFPoint[x][y].x = newX;
			else
			{
				ppPoint[x][y].y = ppFPoint[x][y].y = 0;
				ppPoint[x][y].x = ppFPoint[x][y].x = 0;
			}

			newY = mid.y + (newRadius * sin(theta));

			if (newY > 0 && newY < nHeight && newX > 0 && newX < nWidth)
				ppPoint[x][y].y = ppFPoint[x][y].y = newY;
			else
			{
				ppPoint[x][y].y = ppFPoint[x][y].y = 0;
				ppPoint[x][y].x = ppFPoint[x][y].x = 0;
			}
		}


	switch(smoothing)
	{
	case FILTER_NONE:
		CGFilters::OffsetFilterAbs(pBmp, ppPoint);		
		break;
	case FILTER_BILINEAR:
		CGFilters::OffsetFilterAntiAlias(pBmp, ppFPoint);		
		break;
	default:
		// Means that an inappropriate value was passed for smoothing)
		assert(false);
	}		

	for (int j = 0; j < pBmp->GetWidth(); ++j)
	{
		delete [] ppPoint[j];
		delete [] ppFPoint[j];
	}

	delete [] ppPoint;
	delete [] ppFPoint;
}

void CGFilters::TimeWarp(CGBitmap * pBmp, BYTE factor, short smoothing)
{
	int nWidth = pBmp->GetWidth(), nHeight = pBmp->GetHeight();

	POINT ** ppPoint = new POINT * [nWidth];
	FLOATPOINT ** ppFPoint = new FLOATPOINT * [nWidth];

	for (int i = 0; i < nWidth; ++i)
	{
		ppFPoint[i] = new FLOATPOINT[nHeight];
		ppPoint[i] = new POINT[nHeight];
	}

	POINT mid;
	mid.x = nWidth/2;
	mid.y = nHeight/2;

	POINT pt;
	pt.x = pt.y = 0;
	double theta, radius;
	double newX, newY;

	for (int x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
		{
			int trueX = x - mid.x;
			int trueY = y - mid.y;
			theta = atan2((trueY),(trueX));

			radius = sqrt(trueX*trueX + trueY*trueY);

			double newRadius = sqrt(radius) * factor;

			newX = mid.x + (newRadius * cos(theta));
			if (newX > 0 && newX < nWidth)
				ppPoint[x][y].x = ppFPoint[x][y].x = newX;
			else
				ppPoint[x][y].x = ppFPoint[x][y].x = 0;

			newY = mid.y + (newRadius * sin(theta));
			if (newY > 0 && newY < nHeight)
				ppPoint[x][y].y = ppFPoint[x][y].y = newY;
			else
				ppPoint[x][y].y = ppFPoint[x][y].y = 0;
		}

	switch(smoothing)
	{
	case FILTER_NONE:
		CGFilters::OffsetFilterAbs(pBmp, ppPoint);	
		break;
	case FILTER_BILINEAR:
		CGFilters::OffsetFilterAntiAlias(pBmp, ppFPoint);	
		break;
	default:
		// Means that an inappropriate value was passed for smoothing)
		assert(false);
	}		

	for (int j = 0; j < pBmp->GetWidth(); ++j)
	{
		delete [] ppPoint[j];
		delete [] ppFPoint[j];
	}

	delete [] ppPoint;
	delete [] ppFPoint;
}

void CGFilters::Moire(CGBitmap * pBmp, double fDegree)
{
	POINT ** ppPoint = new POINT * [pBmp->GetWidth()];

	for (int i = 0; i < pBmp->GetWidth(); ++i)
		ppPoint[i] = new POINT[pBmp->GetHeight()];

	int nWidth = pBmp->GetWidth(), nHeight = pBmp->GetHeight();

	POINT mid;
	mid.x = nWidth/2;
	mid.y = nHeight/2;

	POINT pt;
	pt.x = pt.y = 0;
	double theta, radius;
	int newX, newY;

	for (int x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
		{
			int trueX = x - mid.x;
			int trueY = y - mid.y;
			theta = atan2((trueX),(trueY));

			radius = sqrt(trueX*trueX + trueY*trueY);

			newX = (int)(radius * sin(theta + fDegree * radius));
			if (newX > 0 && newX < nWidth)
				ppPoint[x][y].x = newX - x;
			else
				ppPoint[x][y].x = 0;

			newY = (int)(radius * sin(theta + fDegree * radius));
			if (newY > 0 && newY < nHeight)
				ppPoint[x][y].y = newY - y;
			else
				ppPoint[x][y].y = 0;
		}

	CGFilters::OffsetFilter(pBmp, ppPoint);		

	for (int j = 0; j < pBmp->GetWidth(); ++j)
		delete [] ppPoint[j];

	delete [] ppPoint;
}

void CGFilters::Water(CGBitmap * pBmp, short nWave, short smoothing /*= CGFilters::FILTER_NONE*/)
{
//	if (!m_bInit) Init();

	int nWidth = pBmp->GetWidth(), nHeight = pBmp->GetHeight();

	POINT ** ppPoint = new POINT * [nWidth];
	FLOATPOINT ** ppFPoint = new FLOATPOINT * [nWidth];

	for (int i = 0; i < nWidth; ++i)
	{
		ppFPoint[i] = new FLOATPOINT[nHeight];
		ppPoint[i] = new POINT[nHeight];
	}

	double newX, newY;
	double xo, yo;

	for (int x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
		{
			xo = ((double)nWave * sin(2.0 * 3.1415 * (float)y / 128.0));
			yo = ((double)nWave * cos(2.0 * 3.1415 * (float)x / 128.0));

			newX = (x + xo);
			newY = (y + yo);

			if (newX > 0 && newX < nWidth)
				ppPoint[x][y].x = ppFPoint[x][y].x = newX;
			else
				ppPoint[x][y].x = ppFPoint[x][y].x = 0;

			if (newY > 0 && newY < nHeight)
				ppPoint[x][y].y = ppFPoint[x][y].y = newY;
			else
				ppPoint[x][y].y = ppFPoint[x][y].y = 0;
		}

	switch(smoothing)
	{
	case FILTER_NONE:
		CGFilters::OffsetFilterAbs(pBmp, ppPoint);		
		break;
	case FILTER_BILINEAR:
		CGFilters::OffsetFilterAntiAlias(pBmp, ppFPoint);		
		break;
	default:
		// Means that an inappropriate value was passed for smoothing)
		assert(false);
	}

	for (int j = 0; j < pBmp->GetWidth(); ++j)
	{
		delete [] ppPoint[j];
		delete [] ppFPoint[j];
	}

	delete [] ppPoint;
	delete [] ppFPoint;
}

void CGFilters::Sketch(CGBitmap * pBmp, short nDegree)
{
	POINT ** ppPoint = new POINT * [pBmp->GetWidth()];

	for (int i = 0; i < pBmp->GetWidth(); ++i)
		ppPoint[i] = new POINT[pBmp->GetHeight()];

	int nWidth = pBmp->GetWidth(), nHeight = pBmp->GetHeight();

	int newX, newY;

	short nHalf = (short)floor(nDegree/2);

	for (int x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
		{
			newX = rand()%nDegree - nHalf;
			
			if (x + newX > 0 && x +newX < nWidth)
				ppPoint[x][y].x = newX;
			else
				ppPoint[x][y].x = 0;

			newY = rand() %nDegree - nHalf;
			
			if (y + newY > 0 && y + newY < nHeight)
				ppPoint[x][y].y = newY;
			else
				ppPoint[x][y].y = 0;
		}

	CGFilters::OffsetFilter(pBmp, ppPoint);		

	for (int j = 0; j < pBmp->GetWidth(); ++j)
		delete [] ppPoint[j];

	delete [] ppPoint;
}

void CGFilters::Pixelate(CGBitmap * pBmp, short pixel, bool bGrid /* = false */)
{
	POINT ** ppPoint = new POINT * [pBmp->GetWidth()];

	for (int i = 0; i < pBmp->GetWidth(); ++i)
		ppPoint[i] = new POINT[pBmp->GetHeight()];

	int nWidth = pBmp->GetWidth(), nHeight = pBmp->GetHeight();

	int newX, newY;

	for (int x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
		{
			newX = pixel - x%pixel;
			
			if (bGrid && newX == pixel)
				ppPoint[x][y].x = -x;
			else if (x + newX > 0 && x +newX < nWidth)
				ppPoint[x][y].x = newX;
			else
				ppPoint[x][y].x = 0;

			newY = pixel - y%pixel;
			
			if (bGrid && newY == pixel)
				ppPoint[x][y].y = -y;
			else if (y + newY > 0 && y + newY < nHeight)
				ppPoint[x][y].y = newY;
			else
				ppPoint[x][y].y = 0;
		}

	CGFilters::OffsetFilter(pBmp, ppPoint);		

	for (int j = 0; j < pBmp->GetWidth(); ++j)
		delete [] ppPoint[j];

	delete [] ppPoint;
}

void CGFilters::OffsetFilterAntiAlias(CGBitmap * pBmp, FLOATPOINT ** ppPoint)
{
	int nPixel = 0;

	switch (pBmp->GetBPP())
	{
	case 16:
		nPixel = 2;
		break;
	case 24:
		nPixel = 3;
		break;
	case 32:
		nPixel = 4;
		break;
	default:
		// No palettised images, please, we're British.
		assert(FALSE);
	}

	const int nYOffset = pBmp->GetWidth() * nPixel + pBmp->m_nStride;

	CGBitmap bmp(pBmp);

	double fraction_x, fraction_y, one_minus_x, one_minus_y;
	int ceil_x, ceil_y, floor_x, floor_y;
	BYTE p1, p2;

	unsigned char * pDest = pBmp->GetBits();
	unsigned char * pSource   = bmp.GetBits();

	int width = pBmp->GetWidth();
	int height = pBmp->GetHeight();

	BYTE red, green, blue;

	for (int x = 0; x < width; ++x)
		for (int y = 0; y < height; ++y)
		{
			if (ppPoint[x][y].x  > width - 2 ||
				ppPoint[x][y].x  < 0.0 ||
				ppPoint[x][y].y  < 0.0 ||
				ppPoint[x][y].y  > height - 2)
				continue;

			// Setup

			floor_x = floor(ppPoint[x][y].x);
			floor_y = floor(ppPoint[x][y].y);
			ceil_x = floor_x + 1;
			ceil_y = floor_y + 1;
			fraction_x = ppPoint[x][y].x - floor_x;
			fraction_y = ppPoint[x][y].y - floor_y;
			one_minus_x = 1.0 - fraction_x;
			one_minus_y = 1.0 - fraction_y;

			switch(pBmp->GetBPP())
			{
			case 16:
				p1 = one_minus_x * (double)((*((WORD*)pSource[floor_y * nYOffset + floor_x * nPixel]) & 0x7C00)>>7) +
					fraction_x * (double)((*((WORD*)pSource[floor_y * nYOffset + ceil_x * nPixel]) & 0x7C00)>>7);

				p2 = one_minus_x * (double)((*((WORD*)pSource[ceil_y * nYOffset + floor_x * nPixel]) & 0x7C00)>>7) +
					fraction_x * (double)((*((WORD*)pSource[ceil_y * nYOffset + nPixel * ceil_x]) & 0x7C00)>>7);

				red = (BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));

				p1 = one_minus_x * (double)((*((WORD*)pSource[floor_y * nYOffset + floor_x * nPixel]) & 0x3E0)>>2) +
					fraction_x * (double)((*((WORD*)pSource[floor_y * nYOffset + ceil_x * nPixel]) & 0x3E0)>>2);

				p2 = one_minus_x * (double)((*((WORD*)pSource[ceil_y * nYOffset + floor_x * nPixel]) & 0x3E0)>>2) +
					fraction_x * (double)((*((WORD*)pSource[ceil_y * nYOffset + nPixel * ceil_x]) & 0x3E0)>>2);

				green = (BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));

				p1 = one_minus_x * (double)((*((WORD*)pSource[floor_y * nYOffset + floor_x * nPixel]) & 0x1F)<<3) +
					fraction_x * (double)((*((WORD*)pSource[floor_y * nYOffset + ceil_x * nPixel]) & 0x1F)<<3);

				p2 = one_minus_x * (double)((*((WORD*)pSource[ceil_y * nYOffset + floor_x * nPixel]) & 0x1F)<<3) +
					fraction_x * (double)((*((WORD*)pSource[ceil_y * nYOffset + nPixel * ceil_x]) & 0x1F)<<3);

				blue = (BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));


				*(WORD*)pDest = (( red >> 3) << 10) |
					((green >> 3) << 5) |
					(blue >> 3);
				break;
			case 24:
			case 32:
				// Blue

				p1 = one_minus_x * (double)(pSource[floor_y * nYOffset + floor_x * nPixel]) +
					fraction_x * (double)(pSource[floor_y * nYOffset + ceil_x * nPixel]);

				p2 = one_minus_x * (double)(pSource[ceil_y * nYOffset + floor_x * nPixel]) +
					fraction_x * (double)(pSource[ceil_y * nYOffset + nPixel * ceil_x]);

				pDest[x * nPixel + y*nYOffset] = (BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));

				// Green

				p1 = one_minus_x * (double)(pSource[floor_y * nYOffset + floor_x * nPixel + 1]) +
					fraction_x * (double)(pSource[floor_y * nYOffset + ceil_x * nPixel + 1]);

				p2 = one_minus_x * (double)(pSource[ceil_y * nYOffset + floor_x * nPixel + 1]) +
					fraction_x * (double)(pSource[ceil_y * nYOffset + nPixel * ceil_x + 1]);
				
				pDest[x * nPixel + y*nYOffset + 1] = (BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));

				// Blue

				p1 = one_minus_x * (double)(pSource[floor_y * nYOffset + floor_x * nPixel + 2]) +
					fraction_x * (double)(pSource[floor_y * nYOffset + ceil_x * nPixel + 2]);

				p2 = one_minus_x * (double)(pSource[ceil_y * nYOffset + floor_x * nPixel + 2]) +
					fraction_x * (double)(pSource[ceil_y * nYOffset + nPixel * ceil_x + 2]);
				
				pDest[x * nPixel + y*nYOffset + 2] = (BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));

				break;
			}
		}
}

void CGFilters::OffsetFilter(CGBitmap * pBmp, POINT ** ppPoint)
{
	int nPixel = 0;

	switch (pBmp->GetBPP())
	{
	case 16:
		nPixel = 2;
		break;
	case 24:
		nPixel = 3;
		break;
	case 32:
		nPixel = 4;
		break;
	default:
		// No palettised images, please, we're British.
		assert(FALSE);
	}

	const int nYOffset = pBmp->GetWidth() * nPixel + pBmp->m_nStride;

	CGBitmap bmp(pBmp);

	unsigned char * pDest = pBmp->GetBits();
	unsigned char * pSource   = bmp.GetBits();

	for (int x = 0; x < pBmp->GetWidth(); ++x)
		for (int y = 0; y < pBmp->GetHeight(); ++y)
		{
			switch(pBmp->GetBPP())
			{
			case 16:
				*(WORD*)pSource[x * nPixel + y*nYOffset] =
				*(WORD*)pDest[(x + ppPoint[x][y].x) * nPixel + (y+ppPoint[x][y].y)*nYOffset];
				break;
			case 24:
			case 32:
				pDest[x * nPixel + y*nYOffset] = 
					pSource[(x + ppPoint[x][y].x) * nPixel + (y+ppPoint[x][y].y)*nYOffset];
				pDest[x * nPixel + y*nYOffset + 1] =
					pSource[(x + ppPoint[x][y].x) * nPixel + (y+ppPoint[x][y].y)*nYOffset + 1];
				pDest[x * nPixel + y*nYOffset + 2] =
					pSource[(x + ppPoint[x][y].x) * nPixel + (y+ppPoint[x][y].y)*nYOffset + 2];
				break;
			}
		}
	
}

void CGFilters::OffsetFilterAbs(CGBitmap * pBmp, POINT ** ppPoint)
{
	int nPixel = 0;

	switch (pBmp->GetBPP())
	{
	case 16:
		nPixel = 2;
		break;
	case 24:
		nPixel = 3;
		break;
	case 32:
		nPixel = 4;
		break;
	default:
		// No palettised images, please, we're British.
		assert(FALSE);
	}

	const int nYOffset = pBmp->GetWidth() * nPixel + pBmp->m_nStride;

	CGBitmap bmp(pBmp);

	unsigned char * pDest = pBmp->GetBits();
	unsigned char * pSource   = bmp.GetBits();

	for (int x = 0; x < pBmp->GetWidth(); ++x)
		for (int y = 0; y < pBmp->GetHeight(); ++y)
		{
			switch(pBmp->GetBPP())
			{
			case 16:
				*(WORD*)pSource[x * nPixel + y*nYOffset] =
				*(WORD*)pDest[(x + ppPoint[x][y].x) * nPixel + (y+ppPoint[x][y].y)*nYOffset];
				break;
			case 24:
			case 32:
				pDest[x * nPixel + y*nYOffset] = 
					pSource[(ppPoint[x][y].x) * nPixel + (ppPoint[x][y].y)*nYOffset];
				pDest[x * nPixel + y*nYOffset + 1] =
					pSource[(ppPoint[x][y].x) * nPixel + (ppPoint[x][y].y)*nYOffset + 1];
				pDest[x * nPixel + y*nYOffset + 2] =
					pSource[(ppPoint[x][y].x) * nPixel + (ppPoint[x][y].y)*nYOffset + 2];
				break;
			}
		}
	
}

void CGFilters::MotionBlur(CGBitmap * pBmp, char offset)
{
	int nPixel = 0;

	switch (pBmp->GetBPP())
	{
	case 16:
		nPixel = 2;
		break;
	case 24:
		nPixel = 3;
		break;
	case 32:
		nPixel = 4;
		break;
	default:
		// No palettised images, please, we're British.
		assert(FALSE);
	}

	CGBitmap bmp(pBmp);

	const int nYOffset = pBmp->GetWidth() * nPixel + pBmp->m_nStride;

	unsigned char * pDest = pBmp->GetBits();
	unsigned char * bits = bmp.GetBits();

	unsigned char red, green, blue;

	int width = pBmp->GetWidth();
	int height = pBmp->GetHeight();

	int nTempOffset = offset;

	for (int y = 0; y < pBmp->GetHeight(); ++y)
	{
		for (int x = 0; x < pBmp->GetWidth()-offset; ++x)
		{
			if (x+offset<0)
				nTempOffset = (x==0) ? 0 : -1;
			else if (x + offset > width)
				nTempOffset = (x==width) ? 0 : 1;
			else
				 nTempOffset = offset;

			switch(pBmp->GetBPP())
			{
			case 16:
				red = (*((WORD*)bits) & 0x7C00)>>7;
				green = (*(WORD*)bits&0x3E0)>>2;
				blue = (*(WORD*)bits&0x001F)<<3;

				red = mid (0, BYTE((*((WORD*)(bits + nYOffset - nPixel * 3))&0x7C00 >> 7) +
					(*((WORD*)(bits + nYOffset - nPixel * 2))&0x7C00 >> 7) +
					(*((WORD*)(bits + nYOffset - nPixel))&0x7C00 >> 7) +
					(*((WORD*)(bits + nYOffset))&0x7C00 >> 7) +
					(*((WORD*)(bits + nYOffset + nPixel))&0x7C00 >> 7) +
					(*((WORD*)(bits + nYOffset + nPixel * 2))&0x7C00 >> 7) +
					(*((WORD*)(bits + nYOffset + nPixel * 3))&0x7C00 >> 7) +
					(*((WORD*)(bits - nYOffset - nPixel))&0x7C00 >> 7) +
					(*((WORD*)(bits - nYOffset - nPixel * 2))&0x7C00 >> 7) +
					(*((WORD*)(bits - nYOffset - nPixel * 3))&0x7C00 >> 7) +
					(*((WORD*)(bits - nYOffset))&0x7C00 >> 7) +
					(*((WORD*)(bits - nYOffset + nPixel))&0x7C00 >> 7) +
					(*((WORD*)(bits - nYOffset + nPixel * 2))&0x7C00 >> 7) +
					(*((WORD*)(bits - nYOffset + nPixel * 3))&0x7C00 >> 7))
					, 255);

				green = mid (0, BYTE((*((WORD*)(bits + nYOffset - nPixel * 3))&0x7C00 >> 2) +
					(*((WORD*)(bits + nYOffset - nPixel * 2))&0x7C00 >> 2) +
					(*((WORD*)(bits + nYOffset - nPixel))&0x7C00 >> 2) +
					(*((WORD*)(bits + nYOffset))&0x7C00 >> 2) +
					(*((WORD*)(bits + nYOffset + nPixel))&0x7C00 >> 2) +
					(*((WORD*)(bits + nYOffset + nPixel * 2))&0x7C00 >> 2) +
					(*((WORD*)(bits + nYOffset + nPixel * 3))&0x7C00 >> 2) +
					(*((WORD*)(bits - nYOffset - nPixel))&0x7C00 >> 2) +
					(*((WORD*)(bits - nYOffset - nPixel * 2))&0x7C00 >> 2) +
					(*((WORD*)(bits - nYOffset - nPixel * 3))&0x7C00 >> 2) +
					(*((WORD*)(bits - nYOffset))&0x7C00 >> 2) +
					(*((WORD*)(bits - nYOffset + nPixel))&0x7C00 >> 2) +
					(*((WORD*)(bits - nYOffset + nPixel * 2))&0x7C00 >> 2) +
					(*((WORD*)(bits - nYOffset + nPixel * 3))&0x7C00 >> 2))
					, 255);

				blue = mid (0, BYTE((*((WORD*)(bits + nYOffset - nPixel * 3))&0x001F << 3) +
					(*((WORD*)(bits + nYOffset - nPixel * 2))&0x001F << 3) +
					(*((WORD*)(bits + nYOffset - nPixel))&0x001F << 3) +
					(*((WORD*)(bits + nYOffset))&0x001F << 3) +
					(*((WORD*)(bits + nYOffset + nPixel))&0x001F << 3) +
					(*((WORD*)(bits + nYOffset + nPixel * 2))&0x001F << 3) +
					(*((WORD*)(bits + nYOffset + nPixel * 3))&0x001F << 3) +
					(*((WORD*)(bits - nYOffset - nPixel))&0x001F << 3) +
					(*((WORD*)(bits - nYOffset - nPixel * 2))&0x001F << 3) +
					(*((WORD*)(bits - nYOffset - nPixel * 3))&0x001F << 3) +
					(*((WORD*)(bits - nYOffset))&0x001F << 3) +
					(*((WORD*)(bits - nYOffset + nPixel))&0x001F << 3) +
					(*((WORD*)(bits - nYOffset + nPixel * 2))&0x001F << 3) +
					(*((WORD*)(bits - nYOffset + nPixel * 3))&0x001F << 3))
					, 255);

				*(WORD*)pDest = (( red >> 3) << 10) |
					((green >> 3) << 5) |
					(blue >> 3);
				
				break;
			default:
				red = bits[2];
				green = bits[1];
				blue = bits[0];

				red = mid (0, ((bits)[2] * 2 +
					(bits + nPixel * nTempOffset)[2])/3, 255);

				green = mid (0, ((bits)[1] * 2 +
					(bits + nPixel * nTempOffset)[1])/3, 255);

				blue = mid (0, ((bits)[0] * 2 +
					(bits + nPixel * nTempOffset)[0])/3, 255);

					pDest[0] = blue;
					pDest[1] = green;
					pDest[2] = red;
			}

			bits += nPixel;
			pDest += nPixel;
		}
		bits += pBmp->m_nStride + nPixel * offset;
		pDest += pBmp->m_nStride + nPixel * offset;
	}
}


void CGFilters::TilePuzzle(CGBitmap * pBmp, BYTE nAcross, BYTE nDown)
{
	POINT ** ppPoint = new POINT * [pBmp->GetWidth()];

	for (int i = 0; i < pBmp->GetWidth(); ++i)
		ppPoint[i] = new POINT[pBmp->GetHeight()];

	const int nWidth = pBmp->GetWidth();
	const int nHeight = pBmp->GetHeight();

	const int nTileWidth = nWidth/nAcross;
	const int nTileHeight = nHeight/nDown;

	std::vector<POINT> vecPoints;

	POINT pt;

	// Just in case M$ conforms to the C++ standard one of these days....
	int x;

	for (x = 0; x < nAcross; ++x)
		for(int y = 0; y < nDown; ++y)
		{
			pt.x = x;
			pt.y = y;
			vecPoints.push_back(pt);
		}

	for (x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
			ppPoint[x][y].x = ppPoint[x][y].y = 0;

	pt.x = pt.y = 0;
	vecPoints.push_back(pt);

	std::random_shuffle(vecPoints.begin(), vecPoints.end());

	pt = vecPoints[0];

	std::vector<POINT>::iterator it = vecPoints.begin();

	int nTileX = 0, nTileY = 0;

	for(;it != vecPoints.end(); ++it)
	{
		for(int y = nTileY; y < min(nTileY + nTileHeight, nHeight); ++y)
		{
			for (x = nTileX; x < min(nWidth, nTileX + nTileWidth); ++x)
			{
				ppPoint[x][y].x = (x - nTileX + it->x * nTileWidth);
				ppPoint[x][y].y = (y - nTileY + it->y * nTileHeight);
			}
		}

		nTileX += nTileWidth;

		if (nTileX >= nWidth - nWidth % nTileWidth)
		{
			nTileX = 0;
			nTileY += nTileHeight;
		}
	}
	
	CGFilters::OffsetFilterAbs(pBmp, ppPoint);		

	for (int j = 0; j < nWidth; ++j)
		delete [] ppPoint[j];

	delete [] ppPoint;	
}

void CGFilters::Antialias(CGBitmap * pBmp)
{
	int ** ppMatrix = new int * [3];
	for (int x = 0; x < 3; ++x)
		ppMatrix[x] = new int[3];
	
	ppMatrix[0][0] = 1;
	ppMatrix[0][1] = 2;
	ppMatrix[0][2] = 1;
	ppMatrix[1][0] = 2;
	ppMatrix[1][1] = 4;
	ppMatrix[1][2] = 2;
	ppMatrix[2][0] = 1;
	ppMatrix[2][1] = 2;
	ppMatrix[2][2] = 1;

	CGFilters::Convolution3x3(pBmp,ppMatrix, 16, 0);

	for (x = 0; x<3; ++x)
		delete [] ppMatrix[x];
	delete [] ppMatrix;

}
void CGFilters::Flip(CGBitmap * pBmp, bool bHorz, bool bVert)
{
	POINT ** ppPoint = new POINT * [pBmp->GetWidth()];

	for (int i = 0; i < pBmp->GetWidth(); ++i)
		ppPoint[i] = new POINT[pBmp->GetHeight()];

	int nWidth = pBmp->GetWidth(), nHeight = pBmp->GetHeight();

	for (int x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
		{
			ppPoint[x][y].x = (bHorz) ? nWidth - (x+1) : x;
			ppPoint[x][y].y = (bVert) ? nHeight - (y + 1) : y;
		}
	
	CGFilters::OffsetFilterAbs(pBmp, ppPoint);		

	for (int j = 0; j < nWidth; ++j)
		delete [] ppPoint[j];

	delete [] ppPoint;	
}

void CGFilters::Histogram(CGBitmap * pBmp, HISTOGRAM_DATA & strHistData)
{
	assert((HBITMAP)*pBmp);

	BYTE red, green, blue, gray;

	unsigned char * bits = pBmp->GetBits();

	int step = 1;

	switch(pBmp->m_BMI.biBitCount)
	{
	case 16:
		step = 2;
		break;
	case 24:
		step = 3;
		break;
	case 32:
		step = 4;
		break;
	default:
		// bitmaps must not have a palette.
		assert(false);
	}

	// Just in case this gets compiled on a non M$ compiler that adheres to the standard
	int y;

	for (y = 0; y < pBmp->m_BMI.biHeight; y++)
	{
		for (int x = 0; x < pBmp->m_BMI.biWidth; ++x)
		{
			if (16 == pBmp->m_BMI.biBitCount)
			{
				red = (*((WORD*)bits) & 0x7C00)>>7;
				green = (*(WORD*)bits&0x3E0)>>2;
				blue = (*(WORD*)bits&0x001F)<<3;				
			}
			else
			{
				red = bits[2];
				green = bits[1];
				blue = bits[0];
			}

			++strHistData.nRed[red];
			++strHistData.nGreen[green];
			++strHistData.nBlue[blue];
			gray = (54 * red + 183 * green + 19 * blue)/256;
			++strHistData.nGray[gray];

			bits += step;
		}

		bits += pBmp->m_nStride;
	}	
	
	if (!strHistData.bFillBitmaps)
		return;

	int nMax = 0;

	for (int i = 0; i < 255; ++i)
	{
		if (strHistData.nRed[i] > nMax)
			nMax = strHistData.nRed[i];
		if (strHistData.nGreen[i] > nMax)
			nMax = strHistData.nGreen[i];
		if (strHistData.nBlue[i] > nMax)
			nMax = strHistData.nBlue[i];
		if (strHistData.nGray[i] > nMax)
			nMax = strHistData.nGray[i];
	}

	strHistData.bmpBlue.Create(260, 260, 24);
	strHistData.bmpGreen.Create(260, 260, 24);
	strHistData.bmpRed.Create(260, 260, 24);
	strHistData.bmpGray.Create(260, 260, 24);

	unsigned char * pRed = strHistData.bmpRed.GetBits();
	unsigned char * pGreen = strHistData.bmpGreen.GetBits();
	unsigned char * pBlue = strHistData.bmpBlue.GetBits();
	unsigned char * pGray = strHistData.bmpGray.GetBits();

	double factor = 256.0 / (double)nMax;

	for (y = 0; y < 260; ++y)
	{
		for (int x = 0; x < 260; ++x)
		{
			if (x < 3 || y < 3)
			{
				pRed[0] = pGreen[0] = pBlue[0] = pGray[0] = strHistData.bBlue;
				pRed[1] = pGreen[1] = pBlue[1] = pGray[1] = strHistData.bGreen;
				pRed[2] = pGreen[2] = pBlue[2] = pGray[2] = strHistData.bRed;
			}
			else
			{
				if ( y - 4 > (int)((double)strHistData.nBlue[x - 4] * factor))
				{
					pBlue[0] = strHistData.bBlue;
					pBlue[1] = strHistData.bGreen;
					pBlue[2] = strHistData.bRed;
				}
				else
				{
					pBlue[0] = strHistData.fBlue;
					pBlue[1] = strHistData.fGreen;
					pBlue[2] = strHistData.fRed;
				}

				if ( y - 4  > (int)((double)strHistData.nRed[x - 4] * factor))
				{
					pRed[0] = strHistData.bBlue;
					pRed[1] = strHistData.bGreen;
					pRed[2] = strHistData.bRed;
				}
				else
				{
					pRed[0] = strHistData.fBlue;
					pRed[1] = strHistData.fGreen;
					pRed[2] = strHistData.fRed;
				}

				if ( y - 4  > (int)((double)strHistData.nGreen[x - 4] * factor))
				{
					pGreen[0] = strHistData.bBlue;
					pGreen[1] = strHistData.bGreen;
					pGreen[2] = strHistData.bRed;
				}
				else
				{
					pGreen[0] = strHistData.fBlue;
					pGreen[1] = strHistData.fGreen;
					pGreen[2] = strHistData.fRed;
				}

				if ( y - 4 > (int)((double)strHistData.nGray[x - 4] * factor))
				{
					pGray[0] = strHistData.bBlue;
					pGray[1] = strHistData.bGreen;
					pGray[2] = strHistData.bRed;
				}
				else
				{
					pGray[0] = strHistData.fBlue;
					pGray[1] = strHistData.fGreen;
					pGray[2] = strHistData.fRed;
				}
			}

			pRed += 3;
			pGreen += 3;
			pBlue += 3;
			pGray += 3;
		}
		pRed += strHistData.bmpRed.m_nStride;
		pGreen += strHistData.bmpGreen.m_nStride;
		pBlue += strHistData.bmpBlue.m_nStride;
		pGray += strHistData.bmpGray.m_nStride;
	}
}

void CGFilters::HistogramEqu(CGBitmap * pBmp, short Type)
{
	HISTOGRAM_DATA data;

	CGFilters::Histogram(pBmp, data);

	const int nWidth = pBmp->GetWidth();
	const int nHeight = pBmp->GetHeight();
	const double nArea = nWidth * nHeight;

	BYTE red, green, blue;

	unsigned char * bits = pBmp->GetBits();

	int step = 1;

	switch(pBmp->m_BMI.biBitCount)
	{
	case 16:
		step = 2;
		break;
	case 24:
		step = 3;
		break;
	case 32:
		step = 4;
		break;
	default:
		// bitmaps must not have a palette.
		assert(false);
	}

	switch (Type)
	{
	case HIST_RED:
		{
//			double dm = 0.0;
			int hist_sum[256];
			int sum = 0;
			
			for (int i = 0; i < 256; ++i)
			{
				sum = sum + data.nRed[i];
				hist_sum[i] = sum;
//				if (data.nRed[i] > 0) ++dm;
			}

		
			for (int y = 0; y < pBmp->m_BMI.biHeight; ++y)
			{
				for (int x = 0; x < pBmp->m_BMI.biWidth; ++x)
				{
					if (16 == pBmp->m_BMI.biBitCount)
					{
						red = (*((WORD*)bits) & 0x7C00)>>7;
						green = (*(WORD*)bits&0x3E0)>>2;
						blue = (*(WORD*)bits&0x001F)<<3;				

						red = (250.0/nArea) * hist_sum[red];

						*(WORD*)bits = (( red >> 3) << 10) |
								((green >> 3) << 5) |
								(blue >> 3);
				
					}
					else
					{
	//					red = bits[2];
	//					green = bits[1];
	//					blue = bits[0];
					
						bits[2] = (250.0/nArea) * hist_sum[bits[2]];					
					}

					bits += step;
				}

				bits += pBmp->m_nStride;
			}	
			
		}
		break;
	case HIST_GREEN:
		{
//			double dm = 0.0;
			int hist_sum[256];
			int sum = 0;
			
			for (int i = 0; i < 256; ++i)
			{
				sum = sum + data.nGreen[i];
				hist_sum[i] = sum;
//				if (data.nGreen[i] > 0) ++dm;
			}

		
			for (int y = 0; y < pBmp->m_BMI.biHeight; ++y)
			{
				for (int x = 0; x < pBmp->m_BMI.biWidth; ++x)
				{
					if (16 == pBmp->m_BMI.biBitCount)
					{
						red = (*((WORD*)bits) & 0x7C00)>>7;
						green = (*(WORD*)bits&0x3E0)>>2;
						blue = (*(WORD*)bits&0x001F)<<3;				

						green = (250.0/nArea) * hist_sum[green];

						*(WORD*)bits = (( red >> 3) << 10) |
								((green >> 3) << 5) |
								(blue >> 3);
				
					}
					else
					{
	//					red = bits[2];
	//					green = bits[1];
	//					blue = bits[0];
					
						bits[1] = (250.0/nArea) * hist_sum[bits[1]];					
					}

					bits += step;
				}

				bits += pBmp->m_nStride;
			}	
			
		}
		break;
	case HIST_BLUE:
		{
//			double dm = 0.0;
			int hist_sum[256];
			int sum = 0;
			
			for (int i = 0; i < 256; ++i)
			{
				sum = sum + data.nBlue[i];
				hist_sum[i] = sum;
//				if (data.nBlue[i] > 0) ++dm;
			}

		
			for (int y = 0; y < pBmp->m_BMI.biHeight; ++y)
			{
				for (int x = 0; x < pBmp->m_BMI.biWidth; ++x)
				{
					if (16 == pBmp->m_BMI.biBitCount)
					{
						red = (*((WORD*)bits) & 0x7C00)>>7;
						green = (*(WORD*)bits&0x3E0)>>2;
						blue = (*(WORD*)bits&0x001F)<<3;				

						blue = (250.0/nArea) * hist_sum[blue];

						*(WORD*)bits = (( red >> 3) << 10) |
								((green >> 3) << 5) |
								(blue >> 3);
				
					}
					else
					{
	//					red = bits[2];
	//					green = bits[1];
	//					blue = bits[0];
					
						bits[0] = (250.0/nArea) * hist_sum[bits[0]];					
					}

					bits += step;
				}

				bits += pBmp->m_nStride;
			}	
			
		}
		break;
	case HIST_ALL:
		{
			int hist_sum_red[256];
			int sumred = 0;
			int hist_sum_green[256];
			int sumgreen = 0;
			int hist_sum_blue[256];
			int sumblue = 0;
			
			for (int i = 0; i < 256; ++i)
			{
				sumred = sumred + data.nRed[i];
				hist_sum_red[i] = sumred;
				sumgreen = sumgreen + data.nGreen[i];
				hist_sum_green[i] = sumgreen;
				sumblue = sumblue + data.nBlue[i];
				hist_sum_blue[i] = sumblue;
			}

		
			for (int y = 0; y < pBmp->m_BMI.biHeight; ++y)
			{
				for (int x = 0; x < pBmp->m_BMI.biWidth; ++x)
				{
					if (16 == pBmp->m_BMI.biBitCount)
					{
						red = (*((WORD*)bits) & 0x7C00)>>7;
						green = (*(WORD*)bits&0x3E0)>>2;
						blue = (*(WORD*)bits&0x001F)<<3;				

						blue = (250.0/nArea) * hist_sum_blue[blue];
						red = (250.0/nArea) * hist_sum_red[red];
						green = (250.0/nArea) * hist_sum_green[green];

						*(WORD*)bits = (( red >> 3) << 10) |
								((green >> 3) << 5) |
								(blue >> 3);
				
					}
					else
					{
						bits[0] = (250.0/nArea) * hist_sum_blue[bits[0]];					
						bits[1] = (250.0/nArea) * hist_sum_green[bits[1]];					
						bits[2] = (250.0/nArea) * hist_sum_red[bits[2]];					
					}

					bits += step;
				}

				bits += pBmp->m_nStride;
			}	
			
		}
		break;
	}
}

/*  Taylors sphere algorithm
void CGFilters::Test(CGBitmap * pBmp, short smoothing)
{
	int nWidth = pBmp->GetWidth(), nHeight = pBmp->GetHeight();

	POINT ** ppPoint = new POINT * [nWidth];
	FLOATPOINT ** ppFPoint = new FLOATPOINT * [nWidth];

	for (int i = 0; i < nWidth; ++i)
	{
		ppFPoint[i] = new FLOATPOINT[nHeight];
		ppPoint[i] = new POINT[nHeight];
	}

	FLOATPOINT mid;
	mid.x = (double)nWidth/2.0;
	mid.y = (double)nHeight/2.0;

	POINT pt;
	pt.x = pt.y = 0;
	double theta;
	double newX, newY;

	const double PiOn2 = 3.1415/2.0;

	for (int x = 0; x < nWidth; ++x)
		for (int y = 0; y < nHeight; ++y)
		{
			theta = asin(((double)x-mid.x)/mid.x);

			newX = (theta + PiOn2) * (mid.x/PiOn2);

			theta = asin(((double)y-mid.y)/mid.y);

			newY = (theta + PiOn2) * (mid.y/PiOn2);
			
			if (newY > 0 && newY < nHeight &&
				newX > 0 && newX < nWidth)
			{
				ppPoint[x][y].x = ppFPoint[x][y]. x = newX;
				ppPoint[x][y].y = ppFPoint[x][y].y = newY;
			}
			else
			{
				ppPoint[x][y].x = ppFPoint[x][y].x = 0;
				ppPoint[x][y].y = ppFPoint[x][y].y = 0;
			}
		}

	switch(smoothing)
	{
	case FILTER_NONE:
		CGFilters::OffsetFilterAbs(pBmp, ppPoint);		
		break;
	case FILTER_BILINEAR:
		CGFilters::OffsetFilterAntiAlias(pBmp, ppFPoint);		
		break;
	default:
		// Means that an inappropriate value was passed for smoothing)
		assert(false);
	}

	for (int j = 0; j < pBmp->GetWidth(); ++j)
	{
		delete [] ppPoint[j];
		delete [] ppFPoint[j];
	}

	delete [] ppPoint;
	delete [] ppFPoint;
}
*/

void CGFilters::NoiseRemovalMean(CGBitmap * pBmp)
{
	const int nWidth = pBmp->GetWidth();
	const int nHeight = pBmp->GetHeight();

	int step = 1;

	switch(pBmp->m_BMI.biBitCount)
	{
	case 16:
		step = 2;
		break;
	case 24:
		step = 3;
		break;
	case 32:
		step = 4;
		break;
	default:
		// bitmaps must not have a palette.
		assert(false);
	}

	CGBitmap bmp(pBmp);

	unsigned char * pDest = pBmp->GetBits();
	unsigned char * pSrc = bmp.GetBits();

	const int nYOffset = nWidth * step + pBmp->m_nStride;

	pSrc += nYOffset;
	pDest += nYOffset;

	BYTE * pByte = new BYTE[9];

	for (int y = 1; y < pBmp->m_BMI.biHeight-1; ++y)
	{
		pSrc += step;
		pDest += step;
		for (int x = 1; x < pBmp->m_BMI.biWidth-1; ++x)
		{
			if (16 == pBmp->m_BMI.biBitCount)
			{
/*				red = (*((WORD*)pSrc) & 0x7C00)>>7;
				green = (*(WORD*)pSrc&0x3E0)>>2;
				blue = (*(WORD*)pSrc&0x001F)<<3;				

				*(WORD*)pDest = (( red >> 3) << 10) |
						((green >> 3) << 5) |
						(blue >> 3);
*/		
			}
			else
			{
				pByte[0] = ((pSrc - nYOffset - step)[0]);
				pByte[1] = ((pSrc - nYOffset)[0]);
				pByte[2] = ((pSrc - nYOffset + step)[0]);
				pByte[3] = ((pSrc - step)[0]);
				pByte[4] = ((pSrc)[0]);
				pByte[5] = ((pSrc + step)[0]);
				pByte[6] = ((pSrc + nYOffset - step)[0]);
				pByte[7] = ((pSrc + nYOffset)[0]);
				pByte[8] = ((pSrc + nYOffset + step)[0]);

				std::sort(pByte, pByte + 9);

				pDest[0] = (BYTE)(std::accumulate(pByte + 2, pByte + 7, 0)/5.0);

				pByte[0] = ((pSrc - nYOffset - step)[1]);
				pByte[1] = ((pSrc - nYOffset)[1]);
				pByte[2] = ((pSrc - nYOffset + step)[1]);
				pByte[3] = ((pSrc - step)[1]);
				pByte[4] = ((pSrc)[1]);
				pByte[5] = ((pSrc + step)[1]);
				pByte[6] = ((pSrc + nYOffset - step)[1]);
				pByte[7] = ((pSrc + nYOffset)[1]);
				pByte[8] = ((pSrc + nYOffset + step)[1]);

				std::sort(pByte, pByte + 9);

				pDest[1] = (BYTE)(std::accumulate(pByte + 2, pByte + 7, 0)/5.0);

				pByte[0] = ((pSrc - nYOffset - step)[2]);
				pByte[1] = ((pSrc - nYOffset)[2]);
				pByte[2] = ((pSrc - nYOffset + step)[2]);
				pByte[3] = ((pSrc - step)[2]);
				pByte[4] = ((pSrc)[2]);
				pByte[5] = ((pSrc + step)[2]);
				pByte[6] = ((pSrc + nYOffset - step)[2]);
				pByte[7] = ((pSrc + nYOffset)[2]);
				pByte[8] = ((pSrc + nYOffset + step)[2]);

				std::sort(pByte, pByte + 9);

				pDest[2] = (BYTE)(std::accumulate(pByte + 2, pByte + 7, 0)/5.0);
			}

			pSrc += step;
			pDest += step;
		}

		pSrc += step;
		pDest += step;
		pSrc += bmp.m_nStride;
		pDest += pBmp->m_nStride;
	}	

	delete [] pByte;
}

void CGFilters::NoiseSaltAndPepper(CGBitmap * pBmp, short Percentage)
{
	const int nWidth = pBmp->GetWidth();
	const int nHeight = pBmp->GetHeight();

	int step = 1;

	switch(pBmp->m_BMI.biBitCount)
	{
	case 16:
		step = 2;
		break;
	case 24:
		step = 3;
		break;
	case 32:
		step = 4;
		break;
	default:
		// bitmaps must not have a palette.
		assert(false);
	}

	unsigned char * pSrc = pBmp->GetBits();

	const int nYOffset = nWidth * step + pBmp->m_nStride;

	srand( (unsigned)time( NULL ) );

	const int iterate = nWidth * nHeight * Percentage/100;

	int x, y;

	BYTE red, green, blue;

	long rand_half = RAND_MAX/2;

	for (int i = 0; i < iterate; ++i)
	{
		x = rand()%nWidth;
		y = rand()%nHeight;

		if (16 == pBmp->m_BMI.biBitCount)
		{
			red = (*(WORD*)(pSrc + (y*nYOffset) + (x * step))& 0x7C00)>>7;
			green = (*(WORD*)(pSrc + (y*nYOffset) + (x * step))&0x3E0)>>2;
			blue = (*(WORD*)(pSrc + (y*nYOffset) + (x * step))&0x001F)<<3;				

			if (rand() > rand_half)
				red = green = blue = 255;
			else
				red = green = blue = 0;

			*(WORD*)(pSrc + (y*nYOffset) + (x * step)) = (( red >> 3) << 10) |
				((green >> 3) << 5) |
				(blue >> 3);
		
			}
			else
			{
				if (rand() > rand_half)
					(pSrc + (y*nYOffset) + (x * step))[0] = (pSrc + (y*nYOffset) + (x * step))[1] = (pSrc + (y*nYOffset) + (x * step))[2] = 255;
				else
					(pSrc + (y*nYOffset) + (x * step))[0] = (pSrc + (y*nYOffset) + (x * step))[1] = (pSrc + (y*nYOffset) + (x * step))[2] = 0;
			}
	}
}

void CGFilters::Test(CGBitmap * pBmp, short RotateDegrees, short nRadius)
{
	const double theta = (double)RotateDegrees * 3.1415/180.0; 
	const int nWidth = pBmp->GetWidth();
	const int nHeight = pBmp->GetHeight();

	int step = 1;

	switch(pBmp->m_BMI.biBitCount)
	{
	case 16:
		step = 2;
		break;
	case 24:
		step = 3;
		break;
	case 32:
		step = 4;
		break;
	default:
		// bitmaps must not have a palette.
		assert(false);
	}

	CGBitmap bmp(pBmp);

	unsigned char * pDest = pBmp->GetBits();
	unsigned char * pSrc = bmp.GetBits();

	const int nYOffset = nWidth * step + pBmp->m_nStride;

	pSrc += nYOffset * nRadius;
	pDest += nYOffset * nRadius;

	BYTE * pByte = new BYTE[9];

	double newX, newY;

	double fraction_x, fraction_y, one_minus_x, one_minus_y;
	int ceil_x, ceil_y, floor_x, floor_y;
	BYTE p1, p2;

	BYTE red, green, blue;

	for (int y = nRadius; y < pBmp->m_BMI.biHeight-nRadius; ++y)
	{
		for (int x = 0; x < pBmp->m_BMI.biWidth; ++x)
		{
			if (16 == pBmp->m_BMI.biBitCount)
			{
/*				red = (*((WORD*)pSrc) & 0x7C00)>>7;
				green = (*(WORD*)pSrc&0x3E0)>>2;
				blue = (*(WORD*)pSrc&0x001F)<<3;				

				*(WORD*)pDest = (( red >> 3) << 10) |
						((green >> 3) << 5) |
						(blue >> 3);
*/		
			}
			else
			{
				newX = nRadius * cos(theta);
				newY = nRadius * sin(theta);

				// Setup

				floor_x = x + floor(newX);
				floor_y = y + floor(newY);
				ceil_x = floor_x + 1;
				ceil_y = floor_y + 1;
				fraction_x = x + newX - floor_x;
				fraction_y = y + newY - floor_y;
				one_minus_x = 1.0 - fraction_x;
				one_minus_y = 1.0 - fraction_y;

				if (floor_x < 0 || floor_y < 0 || ceil_x >= nWidth || ceil_y >= nHeight)
					continue;
				try{
				p1 = one_minus_x * (double)(pSrc[floor_y * nYOffset + floor_x * step]) +
					fraction_x * (double)(pSrc[floor_y * nYOffset + ceil_x * step]);

				p2 = one_minus_x * (double)(pSrc[ceil_y * nYOffset + floor_x * step]) +
					fraction_x * (double)(pSrc[ceil_y * nYOffset + step * ceil_x]);

				blue = (BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));

				p1 = one_minus_x * (double)(pSrc[floor_y * nYOffset + floor_x * step + 1]) +
					fraction_x * (double)(pSrc[floor_y * nYOffset + ceil_x * step + 1]);

				p2 = one_minus_x * (double)(pSrc[ceil_y * nYOffset + floor_x * step + 1]) +
					fraction_x * (double)(pSrc[ceil_y * nYOffset + step * ceil_x + 1]);

				green = (BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));

				p1 = one_minus_x * (double)(pSrc[floor_y * nYOffset + floor_x * step + 2]) +
					fraction_x * (double)(pSrc[floor_y * nYOffset + ceil_x * step + 2]);

				p2 = one_minus_x * (double)(pSrc[ceil_y * nYOffset + floor_x * step + 2]) +
					fraction_x * (double)(pSrc[ceil_y * nYOffset + step * ceil_x + 2]);

				red = (BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2));

				// Setup for other pixel

				floor_x = x - floor(newX);
				floor_y = y - floor(newY);
				ceil_x = floor_x + 1;
				ceil_y = floor_y + 1;
				fraction_x = x - newX - floor_x;
				fraction_y = y - newY - floor_y;
				one_minus_x = 1.0 - fraction_x;
				one_minus_y = 1.0 - fraction_y;

				if (floor_x < 0 || floor_y < 0 || ceil_x >= nWidth || ceil_y >= nHeight)
					continue;

				p1 = one_minus_x * (double)(pSrc[floor_y * nYOffset + floor_x * step]) +
					fraction_x * (double)(pSrc[floor_y * nYOffset + ceil_x * step]);

				p2 = one_minus_x * (double)(pSrc[ceil_y * nYOffset + floor_x * step]) +
					fraction_x * (double)(pSrc[ceil_y * nYOffset + step * ceil_x]);

				pDest[nYOffset * y + x * step] = ((BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2))) 
					- blue + 127;

				p1 = one_minus_x * (double)(pSrc[floor_y * nYOffset + floor_x * step + 1]) +
					fraction_x * (double)(pSrc[floor_y * nYOffset + ceil_x * step + 1]);

				p2 = one_minus_x * (double)(pSrc[ceil_y * nYOffset + floor_x * step + 1]) +
					fraction_x * (double)(pSrc[ceil_y * nYOffset + step * ceil_x + 1]);

				pDest[nYOffset * y + x * step + 1] = ((BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2))) 
					- green + 127;

				p1 = one_minus_x * (double)(pSrc[floor_y * nYOffset + floor_x * step + 2]) +
					fraction_x * (double)(pSrc[floor_y * nYOffset + ceil_x * step + 2]);

				p2 = one_minus_x * (double)(pSrc[ceil_y * nYOffset + floor_x * step + 2]) +
					fraction_x * (double)(pSrc[ceil_y * nYOffset + step * ceil_x + 2]);

				pDest[nYOffset * y + x * step + 2] = ((BYTE)(one_minus_y * (double)(p1) + fraction_y * (double)(p2))) 
					- red + 127;
				}
				catch(...)
				{
					int i=0;
				}

/*					pDest[nYOffset * y + x * step] = (pSrc[int(nYOffset * (y+newY) + (x+newX) * step)] - 
						pSrc[int(nYOffset * (y-newY) + (x-newX) * step)]) + 127;
					pDest[nYOffset * y + x * step + 1] = (pSrc[int(nYOffset * (y+newY) + (x+newX) * step) + 1] - 
						pSrc[int(nYOffset * (y-newY) + (x-newX) * step) + 1]) + 127;
					pDest[nYOffset * y + x * step + 2] = (pSrc[int(nYOffset * (y+newY) + (x+newX) * step) + 2] - 
						pSrc[int(nYOffset * (y-newY) + (x-newX) * step) + 2]) + 127;
*/			}
		}
	}	

	delete [] pByte;
}		

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Christian Graus
Software Developer (Senior)
Australia Australia
Programming computers ( self taught ) since about 1984 when I bought my first Apple ][. Was working on a GUI library to interface Win32 to Python, and writing graphics filters in my spare time, and then building n-tiered apps using asp, atl and asp.net in my job at Dytech. After 4 years there, I've started working from home, at first for Code Project and now for a vet telemedicine company. I owned part of a company that sells client education software in the vet market, but we sold that and I worked for the owners for five years before leaving to get away from the travel, and spend more time with my family. I now work for a company here in Hobart, doing all sorts of Microsoft based stuff in C++ and C#, with a lot of T-SQL in the mix.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 29 Jun 2004
Article Copyright 2004 by Christian Graus
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid