www.pudn.com > BloodVessel.rar > ximadsp.cpp


// xImaDsp.cpp : DSP functions 
/* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it 
 * CxImage version 5.99c 17/Oct/2004 
 */ 
 
#include "ximage.h" 
 
#include "ximaiter.h" 
 
#if CXIMAGE_SUPPORT_DSP 
 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Converts the image to B&W. 
 * The Mean() function can be used for calculating the optimal threshold. 
 * \param level: the lightness threshold. 
 * \return true if everything is ok 
 */ 
bool CxImage::Threshold(BYTE level) 
{ 
	if (!pDib) return false; 
	if (head.biBitCount == 1) return true; 
 
	GrayScale(); 
 
	CxImage tmp(head.biWidth,head.biHeight,1); 
	if (!tmp.IsValid()) return false; 
 
	for (long y=0;ylevel) 
				tmp.SetPixelIndex(x,y,1); 
			else 
				tmp.SetPixelIndex(x,y,0); 
		} 
	} 
	tmp.SetPaletteColor(0,0,0,0); 
	tmp.SetPaletteColor(1,255,255,255); 
	Transfer(tmp); 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Extract RGB channels from the image. Each channel is an 8 bit grayscale image.  
 * \param r,g,b: pointers to CxImage objects, to store the splited channels 
 * \return true if everything is ok 
 */ 
bool CxImage::SplitRGB(CxImage* r,CxImage* g,CxImage* b) 
{ 
	if (!pDib) return false; 
	if (r==NULL && g==NULL && b==NULL) return false; 
 
	CxImage tmpr(head.biWidth,head.biHeight,8); 
	CxImage tmpg(head.biWidth,head.biHeight,8); 
	CxImage tmpb(head.biWidth,head.biHeight,8); 
 
	RGBQUAD color; 
	for(long y=0; yTransfer(tmpr); 
	if (g) g->Transfer(tmpg); 
	if (b) b->Transfer(tmpb); 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Extract CMYK channels from the image. Each channel is an 8 bit grayscale image.  
 * \param c,m,y,k: pointers to CxImage objects, to store the splited channels 
 * \return true if everything is ok 
 */ 
bool CxImage::SplitCMYK(CxImage* c,CxImage* m,CxImage* y,CxImage* k) 
{ 
	if (!pDib) return false; 
	if (c==NULL && m==NULL && y==NULL && k==NULL) return false; 
 
	CxImage tmpc(head.biWidth,head.biHeight,8); 
	CxImage tmpm(head.biWidth,head.biHeight,8); 
	CxImage tmpy(head.biWidth,head.biHeight,8); 
	CxImage tmpk(head.biWidth,head.biHeight,8); 
 
	RGBQUAD color; 
	for(long yy=0; yyTransfer(tmpc); 
	if (m) m->Transfer(tmpm); 
	if (y) y->Transfer(tmpy); 
	if (k) k->Transfer(tmpk); 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Extract YUV channels from the image. Each channel is an 8 bit grayscale image.  
 * \param y,u,v: pointers to CxImage objects, to store the splited channels 
 * \return true if everything is ok 
 */ 
bool CxImage::SplitYUV(CxImage* y,CxImage* u,CxImage* v) 
{ 
	if (!pDib) return false; 
	if (y==NULL && u==NULL && v==NULL) return false; 
 
	CxImage tmpy(head.biWidth,head.biHeight,8); 
	CxImage tmpu(head.biWidth,head.biHeight,8); 
	CxImage tmpv(head.biWidth,head.biHeight,8); 
 
	RGBQUAD color; 
	for(long yy=0; yyTransfer(tmpy); 
	if (u) u->Transfer(tmpu); 
	if (v) v->Transfer(tmpv); 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Extract YIQ channels from the image. Each channel is an 8 bit grayscale image.  
 * \param y,i,q: pointers to CxImage objects, to store the splited channels 
 * \return true if everything is ok 
 */ 
bool CxImage::SplitYIQ(CxImage* y,CxImage* i,CxImage* q) 
{ 
	if (!pDib) return false; 
	if (y==NULL && i==NULL && q==NULL) return false; 
 
	CxImage tmpy(head.biWidth,head.biHeight,8); 
	CxImage tmpi(head.biWidth,head.biHeight,8); 
	CxImage tmpq(head.biWidth,head.biHeight,8); 
 
	RGBQUAD color; 
	for(long yy=0; yyTransfer(tmpy); 
	if (i) i->Transfer(tmpi); 
	if (q) q->Transfer(tmpq); 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Extract XYZ channels from the image. Each channel is an 8 bit grayscale image.  
 * \param x,y,z: pointers to CxImage objects, to store the splited channels 
 * \return true if everything is ok 
 */ 
bool CxImage::SplitXYZ(CxImage* x,CxImage* y,CxImage* z) 
{ 
	if (!pDib) return false; 
	if (x==NULL && y==NULL && z==NULL) return false; 
 
	CxImage tmpx(head.biWidth,head.biHeight,8); 
	CxImage tmpy(head.biWidth,head.biHeight,8); 
	CxImage tmpz(head.biWidth,head.biHeight,8); 
 
	RGBQUAD color; 
	for(long yy=0; yyTransfer(tmpx); 
	if (y) y->Transfer(tmpy); 
	if (z) z->Transfer(tmpz); 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Extract HSL channels from the image. Each channel is an 8 bit grayscale image.  
 * \param h,s,l: pointers to CxImage objects, to store the splited channels 
 * \return true if everything is ok 
 */ 
bool CxImage::SplitHSL(CxImage* h,CxImage* s,CxImage* l) 
{ 
	if (!pDib) return false; 
	if (h==NULL && s==NULL && l==NULL) return false; 
 
	CxImage tmph(head.biWidth,head.biHeight,8); 
	CxImage tmps(head.biWidth,head.biHeight,8); 
	CxImage tmpl(head.biWidth,head.biHeight,8); 
 
	RGBQUAD color; 
	for(long y=0; yTransfer(tmph); 
	if (s) s->Transfer(tmps); 
	if (l) l->Transfer(tmpl); 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
#define  HSLMAX   255	/* H,L, and S vary over 0-HSLMAX */ 
#define  RGBMAX   255   /* R,G, and B vary over 0-RGBMAX */ 
                        /* HSLMAX BEST IF DIVISIBLE BY 6 */ 
                        /* RGBMAX, HSLMAX must each fit in a BYTE. */ 
/* Hue is undefined if Saturation is 0 (grey-scale) */ 
/* This value determines where the Hue scrollbar is */ 
/* initially set for achromatic colors */ 
#define HSLUNDEFINED (HSLMAX*2/3) 
//////////////////////////////////////////////////////////////////////////////// 
RGBQUAD CxImage::RGBtoHSL(RGBQUAD lRGBColor) 
{ 
	BYTE R,G,B;					/* input RGB values */ 
	BYTE H,L,S;					/* output HSL values */ 
	BYTE cMax,cMin;				/* max and min RGB values */ 
	WORD Rdelta,Gdelta,Bdelta;	/* intermediate value: % of spread from max*/ 
 
	R = lRGBColor.rgbRed;	/* get R, G, and B out of DWORD */ 
	G = lRGBColor.rgbGreen; 
	B = lRGBColor.rgbBlue; 
 
	cMax = max( max(R,G), B);	/* calculate lightness */ 
	cMin = min( min(R,G), B); 
	L = (BYTE)((((cMax+cMin)*HSLMAX)+RGBMAX)/(2*RGBMAX)); 
 
	if (cMax==cMin){			/* r=g=b --> achromatic case */ 
		S = 0;					/* saturation */ 
		H = HSLUNDEFINED;		/* hue */ 
	} else {					/* chromatic case */ 
		if (L <= (HSLMAX/2))	/* saturation */ 
			S = (BYTE)((((cMax-cMin)*HSLMAX)+((cMax+cMin)/2))/(cMax+cMin)); 
		else 
			S = (BYTE)((((cMax-cMin)*HSLMAX)+((2*RGBMAX-cMax-cMin)/2))/(2*RGBMAX-cMax-cMin)); 
		/* hue */ 
		Rdelta = (WORD)((((cMax-R)*(HSLMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin)); 
		Gdelta = (WORD)((((cMax-G)*(HSLMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin)); 
		Bdelta = (WORD)((((cMax-B)*(HSLMAX/6)) + ((cMax-cMin)/2) ) / (cMax-cMin)); 
 
		if (R == cMax) 
			H = (BYTE)(Bdelta - Gdelta); 
		else if (G == cMax) 
			H = (BYTE)((HSLMAX/3) + Rdelta - Bdelta); 
		else /* B == cMax */ 
			H = (BYTE)(((2*HSLMAX)/3) + Gdelta - Rdelta); 
 
//		if (H < 0) H += HSLMAX;     //always false 
		if (H > HSLMAX) H -= HSLMAX; 
	} 
	RGBQUAD hsl={L,S,H,0}; 
	return hsl; 
} 
//////////////////////////////////////////////////////////////////////////////// 
float CxImage::HueToRGB(float n1,float n2, float hue) 
{ 
	// fixed implementation for HSL2RGB routine 
	float rValue; 
 
	if (hue > 360) 
		hue = hue - 360; 
	else if (hue < 0) 
		hue = hue + 360; 
 
	if (hue < 60) 
		rValue = n1 + (n2-n1)*hue/60.0f; 
	else if (hue < 180) 
		rValue = n2; 
	else if (hue < 240) 
		rValue = n1+(n2-n1)*(240-hue)/60; 
	else 
		rValue = n1; 
 
	return rValue; 
} 
//////////////////////////////////////////////////////////////////////////////// 
RGBQUAD CxImage::HSLtoRGB(COLORREF cHSLColor) 
{ 
	return HSLtoRGB(RGBtoRGBQUAD(cHSLColor)); 
} 
//////////////////////////////////////////////////////////////////////////////// 
RGBQUAD CxImage::HSLtoRGB(RGBQUAD lHSLColor) 
{  
	// fixed implementation for HSL2RGB routine 
	float h,s,l; 
	float m1,m2; 
	BYTE r,g,b; 
 
	h = (float)lHSLColor.rgbRed * 360.0f/255.0f; 
	s = (float)lHSLColor.rgbGreen/255.0f; 
	l = (float)lHSLColor.rgbBlue/255.0f; 
 
	if (l <= 0.5)	m2 = l * (1+s); 
	else			m2 = l + s - l*s; 
 
	m1 = 2 * l - m2; 
 
	if (s == 0) { 
		r=g=b=(BYTE)(l*255.0f); 
	} else { 
		r = (BYTE)(HueToRGB(m1,m2,h+120) * 255.0f); 
		g = (BYTE)(HueToRGB(m1,m2,h) * 255.0f); 
		b = (BYTE)(HueToRGB(m1,m2,h-120) * 255.0f); 
	} 
 
	RGBQUAD rgb = {b,g,r,0}; 
	return rgb; 
} 
//////////////////////////////////////////////////////////////////////////////// 
RGBQUAD CxImage::YUVtoRGB(RGBQUAD lYUVColor) 
{ 
	int U,V,R,G,B; 
	float Y = lYUVColor.rgbRed; 
	U = lYUVColor.rgbGreen - 128; 
	V = lYUVColor.rgbBlue - 128; 
 
//	R = (int)(1.164 * Y + 2.018 * U); 
//	G = (int)(1.164 * Y - 0.813 * V - 0.391 * U); 
//	B = (int)(1.164 * Y + 1.596 * V); 
	R = (int)( Y + 1.403f * V); 
	G = (int)( Y - 0.344f * U - 0.714f * V); 
	B = (int)( Y + 1.770f * U); 
 
	R= min(255,max(0,R)); 
	G= min(255,max(0,G)); 
	B= min(255,max(0,B)); 
	RGBQUAD rgb={(BYTE)B,(BYTE)G,(BYTE)R,0}; 
	return rgb; 
} 
//////////////////////////////////////////////////////////////////////////////// 
RGBQUAD CxImage::RGBtoYUV(RGBQUAD lRGBColor) 
{ 
	int Y,U,V,R,G,B; 
	R = lRGBColor.rgbRed; 
	G = lRGBColor.rgbGreen; 
	B = lRGBColor.rgbBlue; 
 
//	Y = (int)( 0.257 * R + 0.504 * G + 0.098 * B); 
//	U = (int)( 0.439 * R - 0.368 * G - 0.071 * B + 128); 
//	V = (int)(-0.148 * R - 0.291 * G + 0.439 * B + 128); 
	Y = (int)(0.299f * R + 0.587f * G + 0.114f * B); 
	U = (int)((B-Y) * 0.565f + 128); 
	V = (int)((R-Y) * 0.713f + 128); 
 
	Y= min(255,max(0,Y)); 
	U= min(255,max(0,U)); 
	V= min(255,max(0,V)); 
	RGBQUAD yuv={(BYTE)V,(BYTE)U,(BYTE)Y,0}; 
	return yuv; 
} 
//////////////////////////////////////////////////////////////////////////////// 
RGBQUAD CxImage::YIQtoRGB(RGBQUAD lYIQColor) 
{ 
	int I,Q,R,G,B; 
	float Y = lYIQColor.rgbRed; 
	I = lYIQColor.rgbGreen - 128; 
	Q = lYIQColor.rgbBlue - 128; 
 
	R = (int)( Y + 0.956f * I + 0.621f * Q); 
	G = (int)( Y - 0.273f * I - 0.647f * Q); 
	B = (int)( Y - 1.104f * I + 1.701f * Q); 
 
	R= min(255,max(0,R)); 
	G= min(255,max(0,G)); 
	B= min(255,max(0,B)); 
	RGBQUAD rgb={(BYTE)B,(BYTE)G,(BYTE)R,0}; 
	return rgb; 
} 
//////////////////////////////////////////////////////////////////////////////// 
RGBQUAD CxImage::RGBtoYIQ(RGBQUAD lRGBColor) 
{ 
	int Y,I,Q,R,G,B; 
	R = lRGBColor.rgbRed; 
	G = lRGBColor.rgbGreen; 
	B = lRGBColor.rgbBlue; 
 
	Y = (int)( 0.2992f * R + 0.5868f * G + 0.1140f * B); 
	I = (int)( 0.5960f * R - 0.2742f * G - 0.3219f * B + 128); 
	Q = (int)( 0.2109f * R - 0.5229f * G + 0.3120f * B + 128); 
 
	Y= min(255,max(0,Y)); 
	I= min(255,max(0,I)); 
	Q= min(255,max(0,Q)); 
	RGBQUAD yiq={(BYTE)Q,(BYTE)I,(BYTE)Y,0}; 
	return yiq; 
} 
//////////////////////////////////////////////////////////////////////////////// 
RGBQUAD CxImage::XYZtoRGB(RGBQUAD lXYZColor) 
{ 
	int X,Y,Z,R,G,B; 
	X = lXYZColor.rgbRed; 
	Y = lXYZColor.rgbGreen; 
	Z = lXYZColor.rgbBlue; 
	double k=1.088751; 
 
	R = (int)(  3.240479f * X - 1.537150f * Y - 0.498535f * Z * k); 
	G = (int)( -0.969256f * X + 1.875992f * Y + 0.041556f * Z * k); 
	B = (int)(  0.055648f * X - 0.204043f * Y + 1.057311f * Z * k); 
 
	R= min(255,max(0,R)); 
	G= min(255,max(0,G)); 
	B= min(255,max(0,B)); 
	RGBQUAD rgb={(BYTE)B,(BYTE)G,(BYTE)R,0}; 
	return rgb; 
} 
//////////////////////////////////////////////////////////////////////////////// 
RGBQUAD CxImage::RGBtoXYZ(RGBQUAD lRGBColor) 
{ 
	int X,Y,Z,R,G,B; 
	R = lRGBColor.rgbRed; 
	G = lRGBColor.rgbGreen; 
	B = lRGBColor.rgbBlue; 
 
	X = (int)( 0.412453f * R + 0.357580f * G + 0.180423f * B); 
	Y = (int)( 0.212671f * R + 0.715160f * G + 0.072169f * B); 
	Z = (int)((0.019334f * R + 0.119193f * G + 0.950227f * B)*0.918483657f); 
 
	//X= min(255,max(0,X)); 
	//Y= min(255,max(0,Y)); 
	//Z= min(255,max(0,Z)); 
	RGBQUAD xyz={(BYTE)Z,(BYTE)Y,(BYTE)X,0}; 
	return xyz; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Generates a "rainbow" palette with saturated colors 
 * \param correction: 1 generates a single hue spectrum. 0.75 is nice for scientific applications. 
 */ 
void CxImage::HuePalette(float correction) 
{ 
	if (head.biClrUsed==0) return; 
 
	for(DWORD j=0; j 1.0f) blend = 1.0f; 
	int a0 = (int)(256*blend); 
	int a1 = 256 - a0; 
 
	bool bFullBlend = false; 
	if (blend > 0.999f)	bFullBlend = true; 
 
	RGBQUAD color,hsl; 
	if (head.biClrUsed==0){ 
 
		long xmin,xmax,ymin,ymax; 
		if (pSelection){ 
			xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; 
			ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; 
		} else { 
			xmin = ymin = 0; 
			xmax = head.biWidth; ymax=head.biHeight; 
		} 
 
		for(long y=ymin; y>8); 
						color.rgbBlue = (BYTE)((hsl.rgbBlue * a0 + color.rgbBlue * a1)>>8); 
						color.rgbGreen = (BYTE)((hsl.rgbGreen * a0 + color.rgbGreen * a1)>>8); 
						SetPixelColor(x,y,color); 
					} 
				} 
			} 
		} 
	} else { 
		for(DWORD j=0; j 
	for (int i=0;i<256;i++)	{ 
		cTable[i] = (BYTE)max(0,min(255,(int)((i-128)*c + brightness))); 
	} 
 
	return Lut(cTable); 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * \return mean lightness of the image. Useful with Threshold() and Light() 
 */ 
float CxImage::Mean() 
{ 
	if (!pDib) return 0; 
 
	CxImage tmp(*this,true); 
	if (!tmp.IsValid()) return false; 
 
	tmp.GrayScale(); 
	float sum=0; 
 
	long xmin,xmax,ymin,ymax; 
	if (pSelection){ 
		xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; 
		ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; 
	} else { 
		xmin = ymin = 0; 
		xmax = head.biWidth; ymax=head.biHeight; 
	} 
	if (xmin==xmax || ymin==ymax) return (float)0.0; 
 
	BYTE *iSrc=tmp.info.pImage; 
	iSrc += tmp.info.dwEffWidth*ymin; // necessary for selections  
 
	for(long y=ymin; y 
		for(long x=xmin; x 0 && (y+kmax-1) < head.biHeight && x-k2 > 0 && (x+kmax-1) < head.biWidth) 
					{ 
						b=0; 
						iCount = 0; 
						iY2 = ((y-k2)*info.dwEffWidth); 
						for(long j=-k2;j r) r=c.rgbRed; 
						if (c.rgbGreen > g) g=c.rgbGreen; 
						if (c.rgbBlue > b) b=c.rgbBlue; 
					} 
				} 
				c.rgbRed   = r; 
				c.rgbGreen = g; 
				c.rgbBlue  = b; 
				tmp.SetPixelColor(x,y,c); 
			} 
		} 
	} 
	Transfer(tmp); 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Enhance the variations between adjacent pixels. 
 * Similar results can be achieved using Filter(), 
 * but the algorithms are different both in Edge() and in Contour(). 
 * \param Ksize: size of the kernel. 
 * \return true if everything is ok 
 */ 
bool CxImage::Edge(long Ksize) 
{ 
	if (!pDib) return false; 
 
	long k2 = Ksize/2; 
	long kmax= Ksize-k2; 
	BYTE r,g,b,rr,gg,bb; 
	RGBQUAD c; 
 
	CxImage tmp(*this,pSelection!=0,true,true); 
	if (!tmp.IsValid()) return false; 
 
	long xmin,xmax,ymin,ymax; 
	if (pSelection){ 
		xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; 
		ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; 
	} else { 
		xmin = ymin = 0; 
		xmax = head.biWidth; ymax=head.biHeight; 
	} 
 
	for(long y=ymin; y r) r=c.rgbRed; 
						if (c.rgbGreen > g) g=c.rgbGreen; 
						if (c.rgbBlue > b) b=c.rgbBlue; 
 
						if (c.rgbRed < rr) rr=c.rgbRed; 
						if (c.rgbGreen < gg) gg=c.rgbGreen; 
						if (c.rgbBlue < bb) bb=c.rgbBlue; 
					} 
				} 
				c.rgbRed   = 255-abs(r-rr); 
				c.rgbGreen = 255-abs(g-gg); 
				c.rgbBlue  = 255-abs(b-bb); 
				tmp.SetPixelColor(x,y,c); 
			} 
		} 
	} 
	Transfer(tmp); 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Blends two images 
 * \param imgsrc2: image to be mixed with this 
 * \param op: blending method; see ImageOpType 
 * \param lXOffset, lYOffset: image displacement 
 * \param bMixAlpha: if true and imgsrc2 has a valid alpha layer, it will be mixed in the destination image. 
 * \return true if everything is ok 
 * 
 * thanks to Mwolski 
 */ 
//  
void CxImage::Mix(CxImage & imgsrc2, ImageOpType op, long lXOffset, long lYOffset, bool bMixAlpha) 
{ 
    long lWide = min(GetWidth(),imgsrc2.GetWidth()-lXOffset); 
    long lHeight = min(GetHeight(),imgsrc2.GetHeight()-lYOffset); 
 
	bool bEditAlpha = imgsrc2.AlphaIsValid() & bMixAlpha; 
 
	if (bEditAlpha && AlphaIsValid()==false){ 
		AlphaCreate(); 
	} 
 
    RGBQUAD rgbBackgrnd = GetTransColor(); 
    RGBQUAD rgb1, rgb2, rgbDest; 
 
    for(long lY=0;lY>8); 
							} else { 
								a=255; 
							} 
 
							if (a==0){ //transparent  
								rgbDest = rgb1;  
							} else if (a==255){ //opaque  
								rgbDest = rgb2;  
							} else { //blend  
								a1 = (BYTE)~a;  
								rgbDest.rgbBlue = (BYTE)((rgb1.rgbBlue*a1+rgb2.rgbBlue*a)>>8);  
								rgbDest.rgbGreen = (BYTE)((rgb1.rgbGreen*a1+rgb2.rgbGreen*a)>>8);  
								rgbDest.rgbRed = (BYTE)((rgb1.rgbRed*a1+rgb2.rgbRed*a)>>8);   
							} 
 
							if (bEditAlpha) rgbDest.rgbReserved = (BYTE)(((1+rgb1.rgbReserved)*a)>>8); 
						}  
						break;  
					case OpSrcBlend: 
						if(memcmp(&rgb1,&rgbBackgrnd,sizeof(RGBQUAD))==0) 
							rgbDest = rgb2; 
						else 
						{ 
							long lBDiff = abs(rgb1.rgbBlue - rgbBackgrnd.rgbBlue); 
							long lGDiff = abs(rgb1.rgbGreen - rgbBackgrnd.rgbGreen); 
							long lRDiff = abs(rgb1.rgbRed - rgbBackgrnd.rgbRed); 
 
							double lAverage = (lBDiff+lGDiff+lRDiff)/3; 
							double lThresh = 16; 
							double dLarge = lAverage/lThresh; 
							double dSmall = (lThresh-lAverage)/lThresh; 
							double dSmallAmt = dSmall*((double)rgb2.rgbBlue); 
 
							if( lAverage < lThresh+1){ 
								rgbDest.rgbBlue = (BYTE)max(0,min(255,(int)(dLarge*((double)rgb1.rgbBlue) + 
												dSmallAmt))); 
								rgbDest.rgbGreen = (BYTE)max(0,min(255,(int)(dLarge*((double)rgb1.rgbGreen) + 
												dSmallAmt))); 
								rgbDest.rgbRed = (BYTE)max(0,min(255,(int)(dLarge*((double)rgb1.rgbRed) + 
												dSmallAmt))); 
							} 
							else 
								rgbDest = rgb1; 
						} 
						break; 
						default: 
						return; 
				} 
				SetPixelColor(lX,lY,rgbDest,bEditAlpha); 
			} 
		} 
	} 
} 
//////////////////////////////////////////////////////////////////////////////// 
// thanks to Kenneth Ballard 
void CxImage::MixFrom(CxImage & imagesrc2, long lXOffset, long lYOffset) 
{ 
    RGBQUAD rgbBackgrnd = imagesrc2.GetTransColor(); 
    RGBQUAD rgb1; 
 
    long width = imagesrc2.GetWidth(); 
    long height = imagesrc2.GetHeight(); 
 
    int x, y; 
 
    for(x = 0; x < width; x++) 
    { 
        for(y = 0; y < height; y++) 
        { 
            rgb1 = imagesrc2.GetPixelColor(x, y); 
            if(memcmp(&rgb1, &rgbBackgrnd, sizeof(RGBQUAD)) != 0) 
                SetPixelColor(x + lXOffset, y + lYOffset, rgb1); 
        } 
    } 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Adjusts separately the red, green, and blue values in the image. 
 * \param r, g, b: can be from -255 to +255. 
 * \return true if everything is ok 
 */ 
bool CxImage::ShiftRGB(long r, long g, long b) 
{ 
	if (!pDib) return false; 
	RGBQUAD color; 
	if (head.biClrUsed==0){ 
 
		long xmin,xmax,ymin,ymax; 
		if (pSelection){ 
			xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; 
			ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; 
		} else { 
			xmin = ymin = 0; 
			xmax = head.biWidth; ymax=head.biHeight; 
		} 
 
		for(long y=ymin; y 
	for (int i=0;i<256;i++)	{ 
		cTable[i] = (BYTE)max(0,min(255,(int)( pow((double)i, dinvgamma) / dMax))); 
	} 
 
	return Lut(cTable); 
} 
//////////////////////////////////////////////////////////////////////////////// 
#if CXIMAGE_SUPPORT_WINCE == 0 
/** 
 * Adjusts the intensity of each pixel to the median intensity of its surrounding pixels. 
 * \param Ksize: size of the kernel. 
 * \return true if everything is ok 
 */ 
bool CxImage::Median(long Ksize) 
{ 
	if (!pDib) return false; 
 
	long k2 = Ksize/2; 
	long kmax= Ksize-k2; 
	long i,j,k; 
 
	RGBQUAD* kernel = (RGBQUAD*)malloc(Ksize*Ksize*sizeof(RGBQUAD)); 
 
	CxImage tmp(*this,pSelection!=0,true,true); 
	if (!tmp.IsValid()) return false; 
 
	long xmin,xmax,ymin,ymax; 
	if (pSelection){ 
		xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; 
		ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; 
	} else { 
		xmin = ymin = 0; 
		xmax = head.biWidth; ymax=head.biHeight; 
	} 
 
	for(long y=ymin; y 
		for(long x=xmin; xGetWidth(); 
		h=srcReal->GetHeight(); 
	} else { 
		w=srcImag->GetWidth(); 
		h=srcImag->GetHeight(); 
	} 
 
	bool bXpow2 = IsPowerof2(w); 
	bool bYpow2 = IsPowerof2(h); 
	//if bForceFFT, width AND height must be powers of 2 
	if (bForceFFT && !(bXpow2 && bYpow2)) { 
		long i; 
		 
		i=0; 
		while((1< copy the image 
	if (srcReal && dstReal) tmpReal->Copy(*srcReal,true,false,false); 
	if (srcImag && dstImag) tmpImag->Copy(*srcImag,true,false,false); 
 
	// dst&&src are empty -> create new one, else turn to GrayScale 
	if (srcReal==0 && dstReal==0){ 
		tmpReal = new CxImage(w,h,8); 
		tmpReal->Clear(0); 
		tmpReal->SetGrayPalette(); 
	} else { 
		if (!tmpReal->IsGrayScale()) tmpReal->GrayScale(); 
	} 
	if (srcImag==0 && dstImag==0){ 
		tmpImag = new CxImage(w,h,8); 
		tmpImag->Clear(0); 
		tmpImag->SetGrayPalette(); 
	} else { 
		if (!tmpImag->IsGrayScale()) tmpImag->GrayScale(); 
	} 
 
	if (!(tmpReal->IsValid() && tmpImag->IsValid())){ 
		if (srcReal==0 && dstReal==0) delete tmpReal; 
		if (srcImag==0 && dstImag==0) delete tmpImag; 
		return false; 
	} 
 
	//resample for FFT, if necessary  
	tmpReal->Resample(w,h,0); 
	tmpImag->Resample(w,h,0); 
 
	//ok, here we have 2 (w x h), grayscale images ready for a FFT 
 
	double* real; 
	double* imag; 
	long j,k,m; 
 
	_complex **grid; 
	//double mean = tmpReal->Mean(); 
	/* Allocate memory for the grid */ 
	grid = (_complex **)malloc(w * sizeof(_complex)); 
	for (k=0;kGetPixelIndex(k,j)-128; 
			grid[k][j].y = tmpImag->GetPixelIndex(k,j)-128; 
		} 
	} 
 
	//DFT buffers 
	double *real2,*imag2; 
	real2 = (double*)malloc(max(w,h) * sizeof(double)); 
	imag2 = (double*)malloc(max(w,h) * sizeof(double)); 
 
	/* Transform the rows */ 
	real = (double *)malloc(w * sizeof(double)); 
	imag = (double *)malloc(w * sizeof(double)); 
 
	m=0; 
	while((1<SetPixelIndex(k,j,(BYTE)max(0,min(255,(nn*(3+log(_cabs(grid[k][j]))))))); 
				if (grid[k][j].x==0){ 
					tmpImag->SetPixelIndex(k,j,(BYTE)max(0,min(255,(128+(atan(grid[k][j].y/0.0000000001)*nn))))); 
				} else { 
					tmpImag->SetPixelIndex(k,j,(BYTE)max(0,min(255,(128+(atan(grid[k][j].y/grid[k][j].x)*nn))))); 
				} 
			} else { 
				tmpReal->SetPixelIndex(k,j,(BYTE)max(0,min(255,(128 + grid[k][j].x*nn)))); 
				tmpImag->SetPixelIndex(k,j,(BYTE)max(0,min(255,(128 + grid[k][j].y*nn)))); 
			} 
		} 
	} 
 
	for (k=0;k> 1; 
	j = 0; 
	for (i=0;i>= 1; 
		} 
		j += k; 
	} 
 
	/* Compute the FFT */ 
	c1 = -1.0; 
	c2 = 0.0; 
	l2 = 1; 
	for (l=0;lGetWidth(); 
	long h = r->GetHeight(); 
 
	Create(w,h,24); 
 
	g->Resample(w,h); 
	b->Resample(w,h); 
 
	if (a) { 
		a->Resample(w,h); 
#if CXIMAGE_SUPPORT_ALPHA 
		AlphaCreate(); 
#endif //CXIMAGE_SUPPORT_ALPHA 
	} 
 
	RGBQUAD c; 
	for (long y=0;y 
		for (long x=0;xGetPixelIndex(x,y); 
			c.rgbGreen=g->GetPixelIndex(x,y); 
			c.rgbBlue=b->GetPixelIndex(x,y); 
			switch (colorspace){ 
			case 1: 
				SetPixelColor(x,y,HSLtoRGB(c)); 
				break; 
			case 2: 
				SetPixelColor(x,y,YUVtoRGB(c)); 
				break; 
			case 3: 
				SetPixelColor(x,y,YIQtoRGB(c)); 
				break; 
			case 4: 
				SetPixelColor(x,y,XYZtoRGB(c)); 
				break; 
			default: 
				SetPixelColor(x,y,c); 
			} 
#if CXIMAGE_SUPPORT_ALPHA 
			if (a) AlphaSet(x,y,a->GetPixelIndex(x,y)); 
#endif //CXIMAGE_SUPPORT_ALPHA 
		} 
	} 
 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Smart blurring to remove small defects, dithering or artifacts. 
 * \param radius: normally between 0.01 and 0.5 
 * \param niterations: should be trimmed with radius, to avoid blurring should be (radius*niterations)<1 
 * \param colorspace: 0 = RGB, 1 = HSL, 2 = YUV, 3 = YIQ, 4 = XYZ  
 * \return true if everything is ok 
 */ 
bool CxImage::Repair(float radius, long niterations, long colorspace) 
{ 
	if (!IsValid()) return false; 
 
	long w = GetWidth(); 
	long h = GetHeight(); 
 
	CxImage r,g,b; 
 
	r.Create(w,h,8); 
	g.Create(w,h,8); 
	b.Create(w,h,8); 
 
	switch (colorspace){ 
	case 1: 
		SplitHSL(&r,&g,&b); 
		break; 
	case 2: 
		SplitYUV(&r,&g,&b); 
		break; 
	case 3: 
		SplitYIQ(&r,&g,&b); 
		break; 
	case 4: 
		SplitXYZ(&r,&g,&b); 
		break; 
	default: 
		SplitRGB(&r,&g,&b); 
	} 
	 
	for (int i=0; iGetWidth()-1; 
	long h = ch->GetHeight()-1; 
 
	double correction,ix,iy,ixx,ixy,iyy,den,num; 
	int x,y,xy0,xp1,xm1,yp1,ym1; 
	for(x=1; xGetPixelIndex(x,y); 
			xm1 = ch->GetPixelIndex(x-1,y); 
			xp1 = ch->GetPixelIndex(x+1,y); 
			ym1 = ch->GetPixelIndex(x,y-1); 
			yp1 = ch->GetPixelIndex(x,y+1); 
 
			ix= (xp1-xm1)/2.0; 
			iy= (yp1-ym1)/2.0; 
			ixx= xp1 - 2.0 * xy0 + xm1; 
			iyy= yp1 - 2.0 * xy0 + ym1; 
			ixy=(ch->GetPixelIndex(x+1,y+1)+ch->GetPixelIndex(x-1,y-1)- 
				 ch->GetPixelIndex(x-1,y+1)-ch->GetPixelIndex(x+1,y-1))/4.0; 
 
			num= (1.0+iy*iy)*ixx - ix*iy*ixy + (1.0+ix*ix)*iyy; 
			den= 1.0+ix*ix+iy*iy; 
			correction = num/den; 
 
			tmp.SetPixelIndex(x,y,(BYTE)min(255,max(0,(xy0 + radius * correction)))); 
		} 
	} 
 
	for (x=0;x<=w;x++){ 
		tmp.SetPixelIndex(x,0,ch->GetPixelIndex(x,0)); 
		tmp.SetPixelIndex(x,h,ch->GetPixelIndex(x,h)); 
	} 
	for (y=0;y<=h;y++){ 
		tmp.SetPixelIndex(0,y,ch->GetPixelIndex(0,y)); 
		tmp.SetPixelIndex(w,y,ch->GetPixelIndex(w,y)); 
	} 
	ch->Transfer(tmp); 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Enhance the variations between adjacent pixels. 
 * Similar results can be achieved using Filter(), 
 * but the algorithms are different both in Edge() and in Contour(). 
 * \return true if everything is ok 
 */ 
bool CxImage::Contour() 
{ 
	if (!pDib) return false; 
 
	long Ksize = 3; 
	long k2 = Ksize/2; 
	long kmax= Ksize-k2; 
	long i,j,k; 
	BYTE maxr,maxg,maxb; 
	RGBQUAD pix1,pix2; 
 
	CxImage tmp(*this,pSelection!=0,true,true); 
	if (!tmp.IsValid()) return false; 
 
	long xmin,xmax,ymin,ymax; 
	if (pSelection){ 
		xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; 
		ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; 
	} else { 
		xmin = ymin = 0; 
		xmax = head.biWidth; ymax=head.biHeight; 
	} 
 
	for(long y=ymin; ymaxb) maxb = pix2.rgbBlue; 
						if ((pix2.rgbGreen-pix1.rgbGreen)>maxg) maxg = pix2.rgbGreen; 
						if ((pix2.rgbRed-pix1.rgbRed)>maxr) maxr = pix2.rgbRed; 
					} 
				} 
				pix1.rgbBlue=(BYTE)(255-maxb); 
				pix1.rgbGreen=(BYTE)(255-maxg); 
				pix1.rgbRed=(BYTE)(255-maxr); 
				tmp.SetPixelColor(x,y,pix1); 
			} 
		} 
	} 
	Transfer(tmp); 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Adds a random offset to each pixel in the image 
 * \param radius: maximum pixel displacement 
 * \return true if everything is ok 
 */ 
bool CxImage::Jitter(long radius) 
{ 
	if (!pDib) return false; 
 
	long nx,ny; 
 
	CxImage tmp(*this,pSelection!=0,true,true); 
	if (!tmp.IsValid()) return false; 
 
	long xmin,xmax,ymin,ymax; 
	if (pSelection){ 
		xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; 
		ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; 
	} else { 
		xmin = ymin = 0; 
		xmax = head.biWidth; ymax=head.biHeight; 
	} 
 
	for(long y=ymin; y y) 
    { 
		for (row = 0; row < y ; row++) 
		{ 
			scale=0; 
			/* find the scale factor */ 
			for (j = 0; j < y ; j++) 
			{ 
				/* if the index is in bounds, add it to the scale counter */ 
				if ((j + cmatrix_length/2 - row >= 0) && 
					(j + cmatrix_length/2 - row < cmatrix_length)) 
					scale += cmatrix[j + cmatrix_length/2 - row]; 
			} 
			for (i = 0; i= row - cmatrix_length/2) && 
						(j <= row + cmatrix_length/2)) 
						sum += cur_col[j*bytes + i] * cmatrix[j]; 
				} 
				dest_col[row*bytes + i] = (BYTE)(0.5f + sum / scale); 
			} 
		} 
    } 
	else 
    { 
		/* for the edge condition, we only use available info and scale to one */ 
		for (row = 0; row < cmatrix_middle; row++) 
		{ 
			/* find scale factor */ 
			scale=0; 
			for (j = cmatrix_middle - row; j0; j--) 
				{ 
					sum += *(ctable_p + *cur_col_p1); 
					cur_col_p1 += bytes; 
					ctable_p += 256; 
				} 
				cur_col_p++; 
				*(dest_col_p++) = (BYTE)(0.5f + sum); 
			} 
		} 
		 
		/* for the edge condition , we only use available info, and scale to one */ 
		for (; row < y; row++) 
		{ 
			/* find scale factor */ 
			scale=0; 
			for (j = 0; j< y-row + cmatrix_middle; j++) 
				scale += cmatrix[j]; 
			for (i = 0; i 255) dest_row[u*3+v] = 255; 
				else  dest_row[u*3+v] = value; 
			} 
		} 
	} 
 
	Transfer(tmp); 
 
	return TRUE; 
} 
//////////////////////////////////////////////////////////////////////////////// 
/** 
 * Apply a look up table to the image.  
 * \param pLut: BYTE[256] look up table 
 * \return true if everything is ok 
 */ 
bool CxImage::Lut(BYTE* pLut) 
{ 
	if (!pDib || !pLut) return false; 
	RGBQUAD color; 
 
	double dbScaler; 
	if (head.biClrUsed==0){ 
 
		long xmin,xmax,ymin,ymax; 
		if (pSelection){ 
			xmin = info.rSelectionBox.left; xmax = info.rSelectionBox.right; 
			ymin = info.rSelectionBox.bottom; ymax = info.rSelectionBox.top; 
		} else { 
			// faster loop for full image 
			BYTE *iSrc=info.pImage; 
			for(unsigned long i=0; i < head.biSizeImage ; i++){ 
				*iSrc++ = pLut[*iSrc]; 
			} 
			return true; 
		} 
 
		dbScaler = 100.0/ymax; 
 
		for(long y=ymin; y 
			for(long x=xmin; x 
	if (!pDib) return false; 
    FloodFill2(x,y,GetPixelColor(x,y),FillColor); 
	return true; 
} 
//////////////////////////////////////////////////////////////////////////////// 
void CxImage::FloodFill2(int x, int y, RGBQUAD old_color, RGBQUAD new_color) 
{ 
	// Fill in the actual pixels.  
	// Function steps recursively until it finds borders (color that is not old_color) 
	if (!IsInside(x,y)) return; 
 
	RGBQUAD r = GetPixelColor(x,y); 
	COLORREF cr = RGB(r.rgbRed,r.rgbGreen,r.rgbBlue); 
 
	if(cr == RGB(old_color.rgbRed,old_color.rgbGreen,old_color.rgbBlue) 
		&& cr != RGB(new_color.rgbRed,new_color.rgbGreen,new_color.rgbBlue) ) { 
 
		// the above if statement, after && is there to prevent 
		// stack overflows.  The program will continue to find  
		// colors if you flood-fill an entire region (entire picture) 
 
		SetPixelColor(x,y,new_color); 
 
		FloodFill2((x+1),y,old_color,new_color); 
		FloodFill2((x-1),y,old_color,new_color); 
		FloodFill2(x,(y+1),old_color,new_color); 
		FloodFill2(x,(y-1),old_color,new_color); 
	} 
}*/ 
/////////////////////////////////////////////////////////////////////////////// 
#endif //CXIMAGE_SUPPORT_DSP