// DIB.cpp

#include "math.h"
#include "DIB.h"

#define BIG_DISTANCE 10000000L

#define DIST(r1,g1,b1,r2,g2,b2) \
	    (long) (3L*(long)((r1)-(r2))*(long)((r1)-(r2)) + \
		    4L*(long)((g1)-(g2))*(long)((g1)-(g2)) + \
		    2L*(long)((b1)-(b2))*(long)((b1)-(b2)))


static unsigned char masktable[] = { 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01 };

CDIB::CDIB(HANDLE hDib,int nBits)
{
	m_pVoid = NULL;
	m_pLinePtr = NULL;
	m_bUseGamma=FALSE;
	width=height=0;
	if(hDib)
	{
		CreateFromHandle(hDib,nBits);
	}

	m_pixel_format=DIB_PIXEL_FORMAT16_RGB;
}

CDIB::~CDIB()
{
	DestroyDIB();
}

void CDIB::DestroyDIB()
{
	if(m_pVoid) free(m_pVoid);
	m_pVoid = NULL;
	if(m_pLinePtr) free(m_pLinePtr);
	m_pLinePtr = NULL;
}

//modified by c_gao: added pixel format support
BOOL CDIB::Create(int width, int height, int bits,PIXEL_FORMAT_16BIT format)
{
	// Free existing image
	DestroyDIB();
//	ASSERT(bits == 24 || bits == 8);

	BITMAPINFOHEADER bmInfo;
	
	memset(&bmInfo,0,sizeof(BITMAPINFOHEADER));
	bmInfo.biSize = sizeof(BITMAPINFOHEADER);
	bmInfo.biWidth = width;
	bmInfo.biHeight = height;
	bmInfo.biPlanes = 1;
	bmInfo.biBitCount = (USHORT)bits;
	if(bits==16 && format!=DIB_PIXEL_FORMAT16_RGB)
		bmInfo.biCompression = BI_BITFIELDS;
	else
		bmInfo.biCompression = BI_RGB;
	m_pixel_format=format;
	return Create(bmInfo);
}
/*
BOOL CDIB::Create16(int width,int height,int bits,PIXEL_FORMAT_16BIT format)
{
	// Free existing image
	DestroyDIB();

	BITMAPINFOHEADER bmInfo;

	memset(&bmInfo,0,sizeof(BITMAPINFOHEADER));
	bmInfo.biSize = sizeof(BITMAPINFOHEADER);
	bmInfo.biWidth = width;
	bmInfo.biHeight = height;
	bmInfo.biPlanes = 1;
	bmInfo.biBitCount = (USHORT)bits;
	bmInfo.biCompression = BI_BITFIELDS;
	return Create(bmInfo);
}
*/

//modified by c_gao: added pixel format support
BOOL CDIB::Create(BITMAPINFOHEADER& bmInfo)
{
	DestroyDIB();
	bytes = (bmInfo.biBitCount*bmInfo.biWidth)>>3;
	height = abs(bmInfo.biHeight);//modified by c_gao(add abs macro)
	width = bmInfo.biWidth;
//	bmInfo.biHeight *= -1;
	while(bytes%4) bytes++;

	int size;
	size = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize(bmInfo) + bytes*height;
	m_pVoid = (void *)malloc(size);
	if(!m_pVoid) return FALSE;

	m_pInfo = (PBITMAPINFO )m_pVoid;
	memcpy((void *)&m_pInfo->bmiHeader,(void *)&bmInfo,sizeof(BITMAPINFOHEADER));
	m_pRGB = (RGBQUAD *)((unsigned char *)m_pVoid + sizeof(BITMAPINFOHEADER)) ;
//////////////////////////////////////////////////////////////////////////
//added pixel format support
	DWORD mask[3];
	switch(m_pixel_format)
	{
	case DIB_PIXEL_FORMAT16_565:
		mask[0]=0xf800;
		mask[1]=0x07e0;
		mask[2]=0x001f;
		memcpy(m_pRGB,mask,sizeof(DWORD)*3);
		break;
	case DIB_PIXEL_FORMAT16_555:
		mask[0]=0x7c00;
		mask[1]=0x03e0;
		mask[2]=0x001f;
		memcpy(m_pRGB,mask,sizeof(DWORD)*3);
		break;
	default:
		break;
	}
//////////////////////////////////////////////////////////////////////////
	m_pBits = (unsigned char *)(m_pVoid) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize();

	int i;
	BYTE **ptr;
	m_pLinePtr = (BYTE **)malloc(sizeof(BYTE *)*height);
	if(!m_pLinePtr) return FALSE;
	for(i=0,ptr=m_pLinePtr; i < height; i++,ptr++)
	{
		//*ptr = (int)(m_pBits)+(i*bytes);
		//*ptr = (int)GetLinePtr(i);
		*ptr = m_pBits + (height-i-1)*bytes;
	}
	m_nFlags = 0;
	return TRUE;
}

void CDIB::SetPalette(unsigned char *palette)
{
	int i,size;
	RGBQUAD *rgb;
	if(!palette) return;
	size = GetPaletteSize();
	for(i=0,rgb = m_pRGB; i < size; i++,rgb++,palette+=3)
	{
		if(m_bUseGamma)
		{
			rgb->rgbRed = Gamma[palette[0]];
			rgb->rgbGreen = Gamma[palette[1]];
			rgb->rgbBlue = Gamma[palette[2]];
		}
		else
		{
			rgb->rgbRed = palette[0];
			rgb->rgbGreen = palette[1];
			rgb->rgbBlue = palette[2];
		}
		rgb->rgbReserved = (BYTE)0;
	}
}

void CDIB::SetPalette(RGBQUAD *pRGB)
{
	int size;
	if(!pRGB) return;
	size = GetPaletteSize();
	memcpy(m_pRGB,pRGB,size*sizeof(RGBQUAD));
}


int CDIB::GetPaletteSize()
{
	return GetPaletteSize(m_pInfo->bmiHeader);
}

int CDIB::GetPaletteSize(BITMAPINFOHEADER& bmInfo)
{
	if(m_pixel_format == DIB_PIXEL_FORMAT16_565 || m_pixel_format == DIB_PIXEL_FORMAT16_555)
		return 3;

	switch(bmInfo.biBitCount)
	{
	case 1:
			return 2;
	case 4:
			return 16;
	case 8:
			return 256;
	default:
			return 0;
	}
}

void CDIB::SetPixel(int x,int y,COLORREF color)
{
	unsigned char *ptr;
//	assert(x >= 0 && y >=0);
//	assert(x < width && y < height);

//	ptr = m_pBits + (y*bytes) + x * 3;
	ptr = (unsigned char *)m_pLinePtr[y];
	ptr += x*3;
	*ptr++ = (unsigned char)GetBValue(color);
	*ptr++ = (unsigned char)GetGValue(color);
	*ptr++ = (unsigned char)GetRValue(color);
}

void CDIB::SetPixel8(int x,int y,unsigned char color)
{
	unsigned char *ptr,*aptr;
//	ASSERT(x >= 0 && y >=0);
//	ASSERT(x < width && y < height);

//	ptr = m_pBits + (y*bytes) + x ;
//	ptr = (unsigned char *)m_pLinePtr[y] ;
	ptr = GetLinePtr(y);
	aptr = ptr;
	ptr += x;
	*ptr = color;
}


COLORREF CDIB::GetPixel(int x,int y)
{
	unsigned char *ptr;
	COLORREF color;
//	ASSERT(x >= 0 && y >=0);
//	ASSERT(x < width && y < height);

//	ptr = m_pBits + (y*bytes) + x * 3;
	ptr = GetLinePtr(y);
	ptr += (x*3);
	color = RGB(*(ptr+2),*(ptr+1),*ptr);
	return color;
}
/*
CBitmap *CDIB::GetTempBitmap(CDC& dc)
{
	HBITMAP hBitmap;
	CBitmap *temp;
//	ASSERT(m_pVoid != NULL);
	hBitmap = CreateDIBitmap(dc.m_hDC,
				(PBITMAPINFOHEADER)m_pInfo,
				CBM_INIT,
				(const void *)m_pBits,
				m_pInfo,
				DIB_RGB_COLORS);

	if(hBitmap == NULL) return NULL;
	temp = new CBitmap(hBitmap);
	return temp;
}

CBitmap *CDIB::GetBitmap(CDC& dc)
{
	HBITMAP hBitmap;
	CBitmap *temp;
//	ASSERT(m_pVoid != NULL);
	hBitmap = CreateDIBitmap(dc.m_hDC,
				(PBITMAPINFOHEADER)m_pInfo,
				CBM_INIT,
				(const void *)m_pBits,
				m_pInfo,
				DIB_RGB_COLORS);

	if(hBitmap == NULL) return NULL;
	temp = new CBitmap(hBitmap);
	if(temp)
	{
		BITMAP bmp;
		LPVOID lpVoid;
		temp->GetBitmap(&bmp);
		lpVoid = malloc(bmp.bmWidthBytes*bmp.bmHeight);
		if(!lpVoid) return NULL;
		temp->GetBitmapBits(bmp.bmWidthBytes*bmp.bmHeight,lpVoid);
		CBitmap *newBmp = new CBitmap;
		newBmp->CreateBitmapIndirect(&bmp);
		newBmp->SetBitmapBits(bmp.bmWidthBytes*bmp.bmHeight,lpVoid);
		free(lpVoid);
		return newBmp;
	}
	else return NULL;

}
*/
void CDIB::CopyLine(int source,int dest)
{
	unsigned char *src,*dst;
//	ASSERT(source <= height && source >= 0);
//	ASSERT(dest <= height && dest >= 0);
	if(source == dest) return;
	src = GetLinePtr(source);
	dst = GetLinePtr(dest);
	memcpy(dst,src,bytes);
}

void CDIB::InitDIB(COLORREF color)
{
	int i,j;
	unsigned char *ptr;
	
	if(m_pInfo->bmiHeader.biBitCount == 24)
	{
		unsigned char col[3];
		col[0]=GetBValue(color);
		col[1]=GetGValue(color);
		col[2]=GetRValue(color);
		for(i=0,ptr = m_pBits; i < height; i++)
		{
			ptr = m_pBits + i*bytes;
			for(j=0; j < width ; j++,ptr+=3)
			{
				memcpy(ptr,col,3);
			}
		}
	}
	else
	{
		for(i=0,ptr = m_pBits; i < height; i++,ptr+=bytes)
		{
			memset(ptr,(BYTE)color,bytes);
		}
	}
}

void CDIB::BitBlt(HDC hDest,int nXDest,int nYDest,int nWidth,
				  int nHeight,int xSrc,int ySrc)
{
	SetDIBitsToDevice(hDest,nXDest,nYDest,nWidth,nHeight,xSrc,
		Height()-ySrc-nHeight,0,Height(),m_pBits,m_pInfo,DIB_RGB_COLORS);
}

void CDIB::StretchBlt(HDC hDest,int nXDest,int nYDest,int nDWidth,
					  int nDHeight,int xSrc,int ySrc,int  nSWidth,int nSHeight)
{
	int err;
	err = StretchDIBits(hDest,nXDest,nYDest,nDWidth,nDHeight,xSrc,ySrc,
		nSWidth,nSHeight,m_pBits,(CONST BITMAPINFO * )&m_pInfo->bmiHeader,DIB_RGB_COLORS,SRCCOPY);
}

void CDIB::ExpandBlt(int nXDest,int nYDest,int xRatio,int yRatio,
					 CDIB& dibSrc,int xSrc,int ySrc,int  nSWidth,int nSHeight)
{
	SetPalette(dibSrc.m_pRGB);

	nSWidth = xSrc+nSWidth > dibSrc.width ? dibSrc.width-xSrc : nSWidth;
	nSHeight = ySrc+nSHeight > dibSrc.height? dibSrc.height-ySrc : nSHeight;

	Expand(nXDest,nYDest,xRatio,yRatio,dibSrc,xSrc,ySrc,nSWidth,nSHeight);
}

void CDIB::Expand(int nXDest,int nYDest,int xRatio,int yRatio,
				  CDIB& dibSrc,int xSrc,int ySrc,int  nSWidth,int nSHeight)
{
	int xNum,yNum,xErr,yErr;	
	int nDWidth,nDHeight;
	
	nDWidth = nSWidth*xRatio;
	nDHeight = nSHeight*yRatio;

	nDWidth = nXDest+nDWidth > width ? width-nXDest : nDWidth ; 
	nDHeight = nYDest+nDHeight > height ? height-nYDest : nDHeight;

	xNum = nDWidth/xRatio;
	yNum = nDHeight/yRatio;
	xErr = nDWidth%xRatio;
	yErr = nDHeight%yRatio;

	unsigned char *buffer,*srcPtr,*destPtr,*ptr;
	int i, j, k = 0;
	
	buffer = (unsigned char *)malloc(nDWidth+20);
	if (!buffer) return;

	for (i = 0; i < yNum; i++,ySrc++)
	{
		srcPtr = dibSrc.GetLinePtr(ySrc) + xSrc;
		ptr = buffer;
		for (j = 0; j < xNum; j++, ptr += xRatio)
		{
			memset(ptr,*(srcPtr+j),xRatio);
			k = *(srcPtr+j);
		}
		memset(ptr, (unsigned char)k, xErr);
		for (j = 0; j < yRatio ; j++, nYDest++)
		{
			destPtr = GetLinePtr(nYDest) + nXDest;
			memcpy(destPtr, buffer, nDWidth);		
		}
	}
	for (j = 0; j < yErr; j++, nYDest++)
	{
		destPtr = GetLinePtr(nYDest) + nXDest;
		memcpy(destPtr, buffer, nDWidth);		
	}
	free(buffer);
}

void CDIB::StretchBlt(int nXDest, int nYDest, int nDWidth, int nDHeight,
					  CDIB& dibSrc, int xSrc, int ySrc, int nSWidth, int nSHeight)
{
	SetPalette(dibSrc.m_pRGB);
	nDWidth = nXDest+nDWidth > width ? width-nXDest : nDWidth ; 
	nDHeight = nYDest+nDHeight > height ? height-nYDest : nDHeight;

	nSWidth = xSrc+nSWidth > dibSrc.width ? dibSrc.width-xSrc : nSWidth;
	nSHeight = ySrc+nSHeight > dibSrc.height? dibSrc.height-ySrc : nSHeight;

	int xDiv,yDiv;
	int xMod,yMod;

	xDiv = nDWidth/nSWidth;
	xMod = nDWidth%nSWidth;

	yDiv = nDHeight/nSHeight;
	yMod = nDHeight%nSHeight;

	if(!xMod && !yMod && xDiv > 0 && yDiv > 0)
	{
		ExpandBlt(nXDest,nYDest,xDiv,yDiv,dibSrc,xSrc,ySrc,nSWidth,nSHeight);
		return;
	}

	unsigned char *tempPtr,*srcPix,*destPix,*q;
	tempPtr = (unsigned char *)malloc(nDWidth+20);
	int i,j,k,l,x,y,m;
	int xErr,yErr;
	for(i=yErr=m=0; i < nSHeight; i++)
	{
		srcPix = dibSrc.GetLinePtr(i+ySrc) + xSrc;
		q = tempPtr;
		for(j=l=xErr=0; j < nSWidth; j++,srcPix++)
		{
			k = xDiv;
			xErr += xMod;
			if(xErr >= nSWidth)
			{
				k++;
				xErr%=nSWidth;
			}
			x=0;
			while(l < nDWidth &&  x < k)
			{
				*q++ = *srcPix;
				l++;
				x++;
			}
		}
		while(l < nDWidth)
		{
			*q++=*srcPix;
			l++;
		}
		k= yDiv;
		yErr += yMod;
		if(yErr >= nSHeight)
		{
			k++;
			yErr%=nSHeight;
		}
		y=0;
		while(m < nDHeight && y < k)
		{
			destPix = GetLinePtr(m+nYDest) + nXDest;
			memcpy(destPix,tempPtr,nDWidth);
			m++;
			y++;
		}
	}
	while(m < nDHeight )
	{
		destPix = GetLinePtr(m+nYDest) + nXDest;
		memcpy(destPix,tempPtr,nDWidth);
		m++;
	}
	free(tempPtr);
}

void CDIB::BitBlt(int nXDest,int nYDest,int nWidth,int nHeight,
				  CDIB& dibSrc,int nSrcX,int nSrcY,BYTE *colors)
{
	SetPalette(dibSrc.m_pRGB);
	if(nXDest < 0)
	{
		nSrcX -= nXDest;
		nWidth += nXDest;
		nXDest=0;
	}
	if(nYDest < 0)
	{
		nSrcY -= nYDest;
		nHeight += nYDest;
		nYDest=0;
	}
	if(nSrcX < 0)
	{
		nXDest -= nSrcX;
		nWidth += nSrcX;
		nSrcX=0;
	}
	if(nSrcY < 0)
	{
		nYDest -= nSrcY;
		nHeight += nSrcY;
		nSrcY=0;
	}
	nWidth = nXDest+nWidth > width ? width-nXDest : nWidth ; 
	nHeight = nYDest+nHeight > height ? height-nYDest : nHeight;

	nWidth = nSrcX+nWidth > dibSrc.width ? dibSrc.width-nSrcX : nWidth;
	nHeight = nSrcY+nHeight > dibSrc.height? dibSrc.height-nSrcY : nHeight;

	nWidth = __max(0,nWidth);
	nHeight = __max(0,nHeight);
	int i,k,l,j;
	unsigned char *srcPtr,*destPtr;
	if(!colors)
	{
		for(i=0,k=nSrcY,l=nYDest; i < nHeight; i++,k++,l++)
		{
			if(k < 0 || l < 0)
			{
				continue;
			}
			else
			{
				srcPtr = dibSrc.GetLinePtr(k);
				destPtr = GetLinePtr(l);
				memcpy(destPtr+nXDest,srcPtr+nSrcX,nWidth);
			}
		}
	}
	else
	{
		for(i=0,k=nSrcY,l=nYDest; i < nHeight; i++,k++,l++)
		{
			if(k < 0 || l < 0)
			{
				continue;
			}
			else
			{
				srcPtr = dibSrc.GetLinePtr(k)+nXDest;
				destPtr = GetLinePtr(l)+nSrcX;
				for(j=0; j < nWidth; j++,srcPtr++,destPtr++)
				{
					if(colors[*srcPtr]) *destPtr=*srcPtr;
				}
			}
		}
	}
}

unsigned char *CDIB::GetLinePtr(int line)
{
/*unsigned char *ptr;
	ptr = m_pBits + (height-line-1)*bytes;
	return ptr;*/
	return m_pLinePtr[line];
}
	
BOOL CDIB::CopyDIB(CDIB& dib)
{
	if(Create(dib.m_pInfo->bmiHeader))
	{
		SetPalette(dib.m_pRGB);
		memcpy(m_pBits,dib.m_pBits,height*bytes);
		return TRUE;
	}
	return FALSE;
}

void CDIB::ReplaceColor(unsigned char oldColor,unsigned char newColor)
{
	int i,j;
	unsigned char *ptr;	
	for(i=0; i < height; i++)
	{
		ptr = GetLinePtr(i);
		for(j=0; j < width; j++)
		{
			if(ptr[j] == oldColor) ptr[j] = newColor;
		}
	}
}


CDIB& CDIB::operator=(CDIB& dib)
{
	CopyDIB(dib);
	return *this;
}

HANDLE CDIB::GetDIBits(int nStartX,int nStartY,int nCx,int nCy)
{
	if(nStartX == -1)
	{
		nStartX = nStartY=0;
		nCx = width;
		nCy = height;
		CDIB dib;
		dib.Create(nCx,nCy,8);
		dib.BitBlt(0,0,nCx,nCy,*this,0,0);
		dib.SetPalette(m_pRGB);
		return dib.DIBHandle();
	}
	return DIBHandle();
}

DWORD CDIB::GetDIBSize()
{
	return sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize() + bytes*height;
}

HANDLE CDIB::DIBHandle()
{
	int nSize;
	HANDLE hMem;
	nSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize() + bytes*height;
	hMem = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE,nSize);
	if(hMem  == NULL) return NULL;
	UCHAR *lpVoid,*pBits;
	LPBITMAPINFOHEADER pHead;
	RGBQUAD *pRgb;
	lpVoid = (UCHAR *)GlobalLock(hMem);
	pHead = (LPBITMAPINFOHEADER )lpVoid;
	memcpy(pHead,&m_pInfo->bmiHeader,sizeof(BITMAPINFOHEADER));
	pRgb = (RGBQUAD *)(lpVoid + sizeof(BITMAPINFOHEADER) );
	memcpy(pRgb,m_pRGB,sizeof(RGBQUAD)*GetPaletteSize());
	pBits = lpVoid + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize();
	memcpy(pBits,m_pBits,height*bytes);
	GlobalUnlock(lpVoid);
	return hMem;
}

BOOL CDIB::CreateFromHandle(HANDLE hMem, int /*bits*/)
{
	DestroyDIB();
	UCHAR *lpVoid,*pBits;
	LPBITMAPINFOHEADER pHead;
	RGBQUAD *pRgb;
	lpVoid = (UCHAR *)GlobalLock(hMem);
	pHead = (LPBITMAPINFOHEADER )lpVoid;
	width = pHead->biWidth;
	height = pHead->biHeight;
	m_nBits = pHead->biBitCount;
	if(pHead->biCompression != BI_RGB) 
	{
		GlobalUnlock(lpVoid);
		return FALSE;
	}
	if(pHead->biBitCount >= 15)
	{
		if(pHead->biBitCount != 24) 
		{
			GlobalUnlock(lpVoid);
			return FALSE;
		}
	}
	if(!Create(*pHead))
	{
		GlobalUnlock(lpVoid);
		return FALSE;
	}
	pRgb = (RGBQUAD *)(lpVoid + sizeof(BITMAPINFOHEADER) );
	memcpy(m_pRGB,pRgb,sizeof(RGBQUAD)*GetPaletteSize());
	pBits = lpVoid + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*GetPaletteSize();
	memcpy(m_pBits,pBits,height*bytes);
	GlobalUnlock(lpVoid);
	return TRUE;
}

void CDIB::UseGamma(float fg,BOOL bUse)
{
	m_bUseGamma = bUse;
	m_fOldGamma = m_fGamma;
	m_fGamma = fg;
	CreateGammaCurve();
}

void CDIB::CreateGammaCurve()
{
	int i;
	for(i=0;i<256;++i)
	{
	    Gamma[i] = (UCHAR)(int)(255 * pow((double)i/255,(double)m_fGamma) + (double)0.5);
	}
}

void CDIB::GetPixel(UINT x,UINT y,int& pixel)
{
//	ASSERT(x < (UINT)Width());
//	ASSERT(y < (UINT)Height());
	if(x >= (UINT)Width()) return;
	if(y >= (UINT)Height()) return;
	pixel=(GetLinePtr(y))[x];
}

BOOL CDIB::Make8Bit(CDIB& dib)
{
	int nBits;	
//	ASSERT(Width() == dib.Width());
//	ASSERT(Height() == dib.Height());
	nBits = dib.GetBitCount();
	switch(nBits)
	{
	case 1:
		return SwitchFromOne(dib);
		break;
	case 4:
		return SwitchFromFour(dib);
		break;
	case 8:
		return SwitchPalette(dib);
		break;
	case 24:
		return SwitchFrom24(dib);
		break;
	default:
		return FALSE;
	}
	return FALSE;
}

BOOL CDIB::SwitchFromOne(CDIB& dib)
{
	int i,j,w,h;
	unsigned char *sPtr,*dPtr;
	unsigned char cols[2];
	w = Width();
	h = Height();
	memset(CachePtr,0,sizeof(CachePtr));
	cols[0] = (UCHAR)ClosestColor(dib.m_pRGB);
	cols[1] = (UCHAR)ClosestColor(dib.m_pRGB+1);
	for (i = 0; i < h; i++)
	{
		dPtr = GetLinePtr(i);
		sPtr = dib.GetLinePtr(i);
		for (j = 0 ; j < w; j++,dPtr++)
		{
			if (!(sPtr[j>>3] & masktable[j&7])) *dPtr = cols[0];
			else *dPtr = cols[1];
		}
	}
	return TRUE;
}

BOOL CDIB::SwitchFromFour(CDIB& dib)
{
	int i,n,j,w,h;
	unsigned char *sPtr,*dPtr;
	unsigned char cols[16];
	w = Width();
	h = Height();
	memset(CachePtr, 0, sizeof(CachePtr));
	for (i = 0; i < 16; i++)
	{
		cols[i] = (UCHAR)ClosestColor(dib.m_pRGB+i);
	}
	for (i = 0; i < h; i++)
	{
		dPtr = GetLinePtr(i);
		sPtr = dib.GetLinePtr(i);
		for (j = 0 ; j < w; j++,dPtr++)
		{
			if (!(j&1)) n = (*sPtr & 0xf0)>>4;
			else 
			{
				n = *sPtr & 0x0f;
				sPtr++;
			}
			*dPtr = cols[n];
		}
	}
	return TRUE;
}

BOOL CDIB::SwitchPalette(CDIB& dib)
{
	int i,j,w,h;
	unsigned char *sPtr,*dPtr;
	unsigned char cols[256];
	w = Width();
	h = Height();
	memset(CachePtr, 0, sizeof(CachePtr));
	for (i = 0; i < 256; i++)
	{
		cols[i] = (UCHAR)ClosestColor(dib.m_pRGB+i);
	}
	for (i = 0; i < h; i++)
	{
		dPtr = GetLinePtr(i);
		sPtr = dib.GetLinePtr(i);
		for (j = 0 ; j < w; j++,sPtr++,dPtr++)
		{
			*dPtr = cols[*sPtr];
		}
	}
	return TRUE;
}

int CDIB::ClosestColor(RGBQUAD *pRgb)
{
	unsigned int dist = BIG_DISTANCE, i, d, c = 0;
	RGBQUAD *pQuad = m_pRGB;
	int pSize = GetPaletteSize();
	for (i = 0; i < (unsigned int)pSize; i++)
	{
		if(CachePtr[i])
		{
			if(!memcmp((void *)&CacheQuad[i], (void *)pRgb,3)) 
			{
				return i;
			}
		}
	}
	for (i = 0; i < (unsigned int)pSize; i++, pQuad++)
	{
		d = Distance(*pRgb,*pQuad);
		if (!d) 
		{
			CacheQuad[i] = *pRgb;
			CachePtr[i] = 1;
			return i;
		}		
		if (dist > d) 
		{
			c = i;
			dist = d;
		}
	}
	CacheQuad[c] = *pRgb;
	CachePtr[c] = 1;
	return c;
}

unsigned int CDIB::Distance(RGBQUAD& rgb1,RGBQUAD& rgb2)
{
	unsigned int d;
	d =  3*(unsigned)((rgb1.rgbRed)-(rgb2.rgbRed))*
		(unsigned)((rgb1.rgbRed)-(rgb2.rgbRed));
	d += 4*(unsigned)((rgb1.rgbGreen)-(rgb2.rgbGreen))*
		(unsigned)((rgb1.rgbGreen)-(rgb2.rgbGreen)) ;
	d += 2*(unsigned)((rgb1.rgbBlue)-(rgb2.rgbBlue))*
		(unsigned)((rgb1.rgbBlue)-(rgb2.rgbBlue));
	return d;
}

int CDIB::CountColors()
{
//	ASSERT(GetBitCount()==8);
	BYTE colors[256],*ptr;
	int nNum=0,i,j,w,d;
	w = Width();
	d = Height();
	memset(colors,0,256);
	for(i=0; i < d; i++)
	{
		ptr = GetLinePtr(i);
		for(j=0; j < w; j++,ptr++)
		{
			if(!colors[*ptr])
			{
				colors[*ptr]=1;
				nNum++;
			}
		}
	}
	return nNum;
}

int CDIB::EnumColors(BYTE *array)
{
//	ASSERT(GetBitCount()==8);
	BYTE *ptr;
	int nNum=0,i,j,w,d;
	w = Width();
	d = Height();
	memset(array,0,256);
	for(i=0; i < d; i++)
	{
		ptr = GetLinePtr(i);
		for(j=0; j < w; j++,ptr++)
		{
			if(!array[*ptr])
			{
				array[*ptr]=1;
				nNum++;
			}
		}
	}
	return nNum;
}

COLORREF CDIB::PaletteColor(int nIndex)
{
//	ASSERT(nIndex < 256);
	RGBQUAD *pRGB= m_pRGB+nIndex;
	return RGB(pRGB->rgbRed,pRGB->rgbGreen,pRGB->rgbBlue);
}

BOOL CDIB::SwitchFrom24(CDIB& dib)
{
	int i,j,w,h,c;
	unsigned char *sPtr,*dPtr;
	BYTE *index_ptr = NULL;
	RGBQUAD rgb;
	w = Width();
	h = Height();
	index_ptr = (BYTE *)malloc(0x7FFF+1);
	if (!index_ptr) return FALSE;
	memset(CachePtr, 0, sizeof(CachePtr));
	for (i = 0; i <= 0x7FFF; i++)
	{
		rgb.rgbRed = (UCHAR)((((i & 0x7C00)>>10) << 3) | 0x07);
		rgb.rgbGreen = (UCHAR)((((i & 0x3e0)>>5) << 3) | 0x07);
		rgb.rgbBlue = (UCHAR)(((i & 0x1F)<<3) | 0x07);
		index_ptr[i] = (UCHAR)ClosestColor(&rgb);
	}
	for (i = 0; i < h; i++)
	{
		dPtr = GetLinePtr(i);
		sPtr = dib.GetLinePtr(i);
		for (j = 0 ; j < w; j++, dPtr++, sPtr += 3)
		{
			c = (*sPtr >> 3) | ((*(sPtr+1) >> 3) << 5) | ((*(sPtr+2) >> 3) << 10);
			*dPtr = index_ptr[c];
		}
	}
	free(index_ptr);
	return TRUE;
}

unsigned char* CDIB::GetBits()
{
	return m_pBits;
}