/******************************************************************
 *  memBMP.cpp
 *
 *  Written by: Shuhua Lai 
 *  Modified by: Fuhua (Frank) Cheng
 *
 * Permission to use, copy, modify, and distribute this file for 
 * commercial usage is prohibited without prior permission of the
 * author. 
 *
 * THE MATERIAL EMBODIED ON THIS PROGRAM IS PROVIDED TO YOU "AS-IS"
 * AND WITHOUT WARRANTY OF ANY KIND, EXPRESS, IMPLIED OR OTHERWISE,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY OR
 * FITNESS FOR A PARTICULAR PURPOSE.  
*******************************************************************/ 

#include "memBMP.h"

memBitmap::~memBitmap() {
	if(bData)
		delete bData;
}

memBitmap::memBitmap() {
	bitData = NULL;
	bData = NULL;
}

memBitmap::memBitmap(int ImageWidth,int ImageHeight) {
	bitData = NULL;
	bData = NULL;
  CreateDirect(ImageWidth, ImageHeight);
}

int memBitmap::HugeRead(int fd,void* buf, int len) {
	int tem=0;
	int offset=0;
	while((tem=read(fd,(char*)buf+offset,len-offset))>0)
	{		
		offset+=tem;		
	}
	return offset;
}

int memBitmap::HugeWrite(int fd, void* buf, int len) {
	int tem = 0;
	int offset =0;
	while((tem = write( fd, (char*)buf+offset, len-offset)) >0)
		offset +=tem;
	return offset;
}

BOOL memBitmap::CreateDirect(int ImageWidth,int ImageHeight) {
	Release();

	BytesPerLine=ImageWidth*3;
	if(BytesPerLine%4!=0)BytesPerLine=(BytesPerLine/4+1)*4;
	lenth=long(ImageHeight)*BytesPerLine;
	bData=new BYTE[lenth+54L];
	if(!bData)
	{
		AfxMessageBox("Failed in malloc");
		return FALSE;
	}

	biWidth=ImageWidth;
	biHeight=ImageHeight;
	biSize=40;
	WORD bmptemp=(WORD)'M';
	bmptemp<<=8;
	bmptemp|=(BYTE)'B';
	bfType=bmptemp;
	bfOffBits=54;
	bfReserved1=0;
	bfReserved2=0;
	biPlanes=1;
	biBitCount=24;
	biCompression=0;
	biXPelsPerMeter=0;
	biYPelsPerMeter=0;
	biClrUsed=0;
	biClrImportant=0;
	bitData=bData+54;
	biSizeImage=lenth;
	bfSize=lenth+54;
	return TRUE;
}

void memBitmap::SetInfo() {	
	BYTE *temp;

	temp = (BYTE *)&bfType;
	SETWV(temp,bData);
	
	temp = (BYTE *)&bfSize;
	SETLV(temp,bData+2);	
	
	temp = (BYTE *)&bfReserved1;
	SETWV(temp,bData+6);
	
	temp = (BYTE *)&bfReserved2;
	SETWV(temp,bData+8);
	
	temp = (BYTE *)&bfOffBits;
	SETLV(temp,bData+10);
	
	temp = (BYTE *)&biSize;
	SETLV(temp,bData+14);
	
	temp = (BYTE *)&biWidth;
	SETLV(temp,bData+18);
	
	temp = (BYTE *)&biHeight;
	SETLV(temp,bData+22);
	
	temp = (BYTE *)&biPlanes;
	SETWV(temp,bData+26);
	
	temp = (BYTE *)&biBitCount;
	SETWV(temp,bData+28);
	
	temp = (BYTE *)&biCompression;
	SETLV(temp,bData+30);
	
	temp = (BYTE *)&biSizeImage;
	SETLV(temp,bData+34);
	
	temp = (BYTE *)&biXPelsPerMeter;
	SETLV(temp,bData+38);
	
	temp = (BYTE *)&biYPelsPerMeter;
	SETLV(temp,bData+42);
	
	temp = (BYTE *)&biClrUsed;
	SETLV(temp,bData+46);
	
	temp = (BYTE *)&biClrImportant;
	SETLV(temp,bData+50);
}

BOOL memBitmap::Load(LPCTSTR filename) {
	char  str[256];
  char  temp;
  int   numchar;
	strcpy(str,filename);
	
	if(strstr(str,".bmp") != NULL)
	{
		int fd;
		if((fd = open(str,FILEMODE)) ==-1)
		{
			AfxMessageBox("Cannot open file");
			return FALSE;
		}
		
		unsigned long int FileLenth = 0;
		BYTE *FileData;
//  lseek(fd,0L,SEEK_END);
    while ((numchar = read(fd, &temp, sizeof(unsigned char)*100)) > 0)
    {
      FileLenth += numchar;
    }
//    printf ("FileLenth = %d", FileLenth);
//    FileLenth=907254;
//		FileLenth=tell(fd);
		lseek(fd,0L,SEEK_SET);
		lenth=FileLenth;
		FileData=new BYTE[lenth];
		bData=FileData;
		if(!FileData)
		{
			AfxMessageBox("Failed in malloc");
			return FALSE;
		}
		if(!HugeRead(fd,FileData,FileLenth))
		{
			AfxMessageBox("Error in reading file");
			return FALSE;
		}

		SetInfo();
		bitData=bData+54;		
		if(((unsigned char)bfType!='B')||
		   ((unsigned char)(bfType>>8)!='M') )
    {
			AfxMessageBox("Not a correct Bmp file");
			return FALSE;
		}
		close(fd);

		if(biCompression!=0)
		{
			AfxMessageBox("No support for compresion file");
			return FALSE;
		}
		BytesPerLine=biWidth*(biBitCount/8);
		if(BytesPerLine%4!=0)BytesPerLine=(BytesPerLine/4+1)*4;
		if(biBitCount==24)return TRUE;
		else if(biBitCount==8)
		{
			BYTE Palette[256][3];
			BYTE *ImageData=bData+bfOffBits;
			int i,j;
			for(i=0;i<256;i++)
			{
				for(j=0;j<3;j++)
				{
					Palette[i][j]=*(bitData+i*4+j);
				}
			}
			int width=biWidth;
			int height=biHeight;
			int OldBytes=BytesPerLine;

			CreateDirect(width,height);
			for(j=0;j>=4;
						Index&=0xf;
					}
					BYTE b=Palette[Index][0];
					BYTE g=Palette[Index][1];
					BYTE r=Palette[Index][2];
					SetPixel(i,height-j-1,RGB(r,g,b));
				}
			}

			delete FileData;
			return TRUE;
		}
		else if(biBitCount==1)
		{
			BYTE Palette[2][3];
			BYTE *ImageData=bData+bfOffBits;
			int i,j;
			for(i=0;i<2;i++)
			{
				for(j=0;j<3;j++)
				{
					Palette[i][j]=*(bitData+i*4+j);
				}
			}
			int width=biWidth;
			int height=biHeight;
			int OldBytes=(width+1)/8;
			if(OldBytes%4!=0)OldBytes=(OldBytes/4+1)*4;

			CreateDirect(width,height);
			for(j=0;j>=(7-i%8);
					Index&=1;
					BYTE b=Palette[Index][0];
					BYTE g=Palette[Index][1];
					BYTE r=Palette[Index][2];
					SetPixel(i,height-j-1,RGB(r,g,b));
				}
			}

			delete FileData;
			return TRUE;
		}
		else
		{
			AfxMessageBox("Illegal color number");
			return FALSE;
		}
	}	
	else
	{
		AfxMessageBox("Not a supported file formate");
		return FALSE;
	}
}

BOOL memBitmap::Save(LPCTSTR filename) {
	char  str[256];
	strcpy(str,filename);
	
	int fd;
	if((fd = open(str,FILEMODE|O_CREAT)) ==-1)
	{
		AfxMessageBox("Cannot open file");
		return FALSE;
	}
	lseek(fd,0L,SEEK_SET);
	HugeWrite(fd,bData,lenth);

	close(fd);
	return TRUE;
}

void memBitmap::Display(int x,int y) {
	int wid = GetWidth();
        int hei = GetHeight();
        int j=0;
        int offset=0;

        BYTE * temp = new BYTE[hei*BytesPerLine];

	glRasterPos2i(x,y);

        for(int i=0;i=biWidth)||
	   (y<0)||(y>=biHeight) )
	{
		AfxMessageBox("Overflow of image rang");
		return RGB(0,0,0);
	}
	y=biHeight-y-1;
	unsigned long temp=(long)y*(long)BytesPerLine+(long)x*3;
	if(temp>=lenth)
	{
		AfxMessageBox("Overflow of file length");
		return RGB(0,0,0);
	}
	unsigned r,g,b;
	b=*(bitData+temp);
	g=*(bitData+temp+1);
	r=*(bitData+temp+2);
	return RGB(r,g,b);
}

void memBitmap::SetPixel(int x,int y,COLORREF color) {

	if((x<0)||(x>=biWidth)||
	   (y<0)||(y>=biHeight) )
	{
		AfxMessageBox("Overflow of image rang");
		return;
	}
	y=biHeight-y-1;
	unsigned long temp=(long)y*(long)BytesPerLine+(long)x*3;
	if(temp>=lenth)
	{
		AfxMessageBox("Overflow of file length");
		return;
	}
	*(bitData+temp)=GetBValue(color);
	*(bitData+temp+1)=GetGValue(color);
	*(bitData+temp+2)=GetRValue(color);
}

BOOL memBitmap::ChangeSize(int cx,int cy) {
	int width=GetWidth();
	int height=GetHeight();
	if( (cx==width)&&(cy==height))return FALSE;	

	double sx,sy;		

	sx=double(cx)/(double)width;
	sy=double(cy)/(double)height;
	int i,j;

	int newBytesPerLine = cx*3;
	if(newBytesPerLine%4!=0)newBytesPerLine=(newBytesPerLine/4+1)*4;

	unsigned char* newdata=new BYTE[long(cy)*newBytesPerLine+54L];
	if(!newdata)
	{
		AfxMessageBox("Failed in malloc");
		return FALSE;
	}
	memset(newdata,0,long(cy)*newBytesPerLine+54L);
	unsigned char* buftemp = newdata + 54;	


	for(i=0;i=width-1)x=width-1;
			if(y<=0)y=0;
			if(y>=height-1)y=height-1;
			if( ((int)x==0)||((int)x==width-1)||((int)y==0)||((int)y==height-1) )
			{
				COLORREF color=GetPixel((int)x,(int)y);
				//SetPixel(i,j,color);
				int xx=i;
				int yy=cy-j-1;
				unsigned long temp=(long)yy*(long)newBytesPerLine+(long)xx*3;
				buftemp[temp]=GetBValue(color);
				buftemp[temp+1]=GetBValue(color);
				buftemp[temp+2]=GetBValue(color);	
			}
			else
			{
				double r1,r2,r3,r4;
				double g1,g2,g3,g4;
				double b1,b2,b3,b4;
				COLORREF c1,c2,c3,c4;
				c1=GetPixel((int)x,(int)y);
				c2=GetPixel((int)x+1,(int)y);
				c3=GetPixel((int)x,(int)y+1);
				c4=GetPixel((int)x+1,(int)y+1);
				double dis1=x-(int)x;
				double dis2=1-x+(int)x;
				double dis3=y-(int)y;
				double dis4=1-y+(int)y;
				r1=GetRValue(c1);
				g1=GetGValue(c1);
				b1=GetBValue(c1);
				r2=GetRValue(c2);
				g2=GetGValue(c2);
				b2=GetBValue(c2);
				r3=GetRValue(c3);
				g3=GetGValue(c3);
				b3=GetBValue(c3);
				r4=GetRValue(c4);
				g4=GetGValue(c4);
				b4=GetBValue(c4);

				int r=int(dis4*(r1*dis2+r2*dis1)+dis3*(r3*dis2+r4*dis1));
				if(r>255)r=255;
				int g=int(dis4*(g1*dis2+g2*dis1)+dis3*(g3*dis2+g4*dis1));
				if(g>255)g=255;
				int b=int(dis4*(b1*dis2+b2*dis1)+dis3*(b3*dis2+b4*dis1));
				if(b>255)b=255;
				
				//SetPixel(i,j,RGB((BYTE)r,(BYTE)g,(BYTE)b));	
				
				int xx=i;
				int yy=cy-j-1;
				unsigned long temp=(long)yy*(long)newBytesPerLine+(long)xx*3;
				buftemp[temp]=b;
				buftemp[temp+1]=g;
				buftemp[temp+2]=r;				
				
			}
		}
	}


	int offset=0;

	BytesPerLine=newBytesPerLine;
	lenth=long(cy)*BytesPerLine;
	
	CopyMemory(newdata,bData,54);
	free(bData);
	bData = newdata;
	SetInfo();

	biWidth=cx;
	biHeight=cy;
	bitData=bData+54;
	biSizeImage=lenth;
	bfSize=lenth+54;
	return TRUE;

}

void memBitmap::Release() {
	if(bData)
		delete bData;	
}

void memBitmap::DoScale(float sw,float sh) {
	ChangeSize((int)(sw*GetWidth()),(int)(sh*GetHeight()));
}

void memBitmap::DoRotate(int jd1) {
	double jd;
	jd=(double)jd1/180;
	jd*=3.14;

//	float scale=m_mBmp.GetScale();
	int width=GetWidth();
	int height=GetHeight();
	int OldDjx=int(sqrt(width*width+height*height)+0.5);//Length of diagonal
	double OldDjxJd=atan((double)height/(double)width); //angle of diagonal and vertical
	double NewDjxJd=OldDjxJd+jd;
	double NewDjxJd1=OldDjxJd-jd;
	int NewWidth=(int)fabs((double)OldDjx*cos(NewDjxJd));
	int NewHeight=(int)fabs((double)OldDjx*sin(NewDjxJd));
	int NewWidth1=(int)fabs((double)OldDjx*cos(NewDjxJd1));
	int NewHeight1=(int)fabs((double)OldDjx*sin(NewDjxJd1));
	NewWidth=MAX(NewWidth,NewWidth1);
	NewHeight=MAX(NewHeight,NewHeight1);
	memBitmap bmp1;
	bmp1.CreateDirect(NewWidth,NewHeight);
	
	int i,j;
	double RotateMatrixElement[4];
	RotateMatrixElement[0]=cos(jd);
	RotateMatrixElement[1]=-sin(jd);
	RotateMatrixElement[2]=sin(jd);
	RotateMatrixElement[3]=cos(jd);
	
	int temp=int((double)width*sin(jd));

	for(i=0;i=0) && (x=0) && (y=width-1)x=width-1;
				if(y<=0)y=0;
				if(y>=height-1)y=height-1;
				if( ((int)x==0)||((int)x==width-1)||((int)y==0)||((int)y==height-1) )
				{
					COLORREF color=GetPixel((int)x,(int)y);
					bmp1.SetPixel(i,j,color);
				}
				else
				{
					double r1,r2,r3,r4;
					double g1,g2,g3,g4;
					double b1,b2,b3,b4;
					COLORREF c1,c2,c3,c4;
					c1=GetPixel((int)x,(int)y);
					c2=GetPixel((int)x+1,(int)y);
					c3=GetPixel((int)x,(int)y+1);
					c4=GetPixel((int)x+1,(int)y+1);
					double dis1=x-(int)x;
					double dis2=1-x+(int)x;
					double dis3=y-(int)y;
					double dis4=1-y+(int)y;
					r1=GetRValue(c1);
					g1=GetGValue(c1);
					b1=GetBValue(c1);
					r2=GetRValue(c2);
					g2=GetGValue(c2);
					b2=GetBValue(c2);
					r3=GetRValue(c3);
					g3=GetGValue(c3);
					b3=GetBValue(c3);
					r4=GetRValue(c4);
					g4=GetGValue(c4);
					b4=GetBValue(c4);

					int r=int(dis4*(r1*dis2+r2*dis1)+dis3*(r3*dis2+r4*dis1));
					if(r>255)r=255;
					int g=int(dis4*(g1*dis2+g2*dis1)+dis3*(g3*dis2+g4*dis1));
					if(g>255)g=255;
					int b=int(dis4*(b1*dis2+b2*dis1)+dis3*(b3*dis2+b4*dis1));
					if(b>255)b=255;
					
					bmp1.SetPixel(i,j,RGB((BYTE)r,(BYTE)g,(BYTE)b));
				}
//				COLORREF color=m_mBmp.GetPixel(x+width/2,y+height/2);
//				bmp1.SetPixel(i,j,color);
			}
			else
			{
				bmp1.SetPixel(i,j,RGB(255,255,255));
			}
		}
	}

	bmp1.Display(0,0);
//	bmp1.Release();
}