#include"Image.h"
#include"octtree.h"
#include<string.h>

#define IMax(x,y) ((x)>(y)?(x):(y))

//>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Image utilities

Point Image::Crop(ImgRGBColor ccl)
{
if(!IsData())  GENNFERR("(Crop)Image does not contain data");
if(itype!=ITYPE_RGB)  GENNFERR("(CropByColor)Image type not supported");

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
RGBPixel *tptr;
ImgRGBColor col;	
int xlow,xhigh,ylow,yhigh;
xlow=xlen*psize; ylow=ylen*Pitch;
yhigh=xhigh=-1;
BYTE *dtp=Lock();

for(y=0;y<ystop;y+=Pitch)
 for(x=0;x<axl;x+=psize)
  {
  tptr=(RGBPixel *)(dtp+x+y);
  col.Set(*tptr,Mask);
  if(col!=ccl) 
   { 
   if(x<xlow)  xlow=x;
   if(y<ylow)  ylow=y;	
   if(x>xhigh) xhigh=x;
   if(y>yhigh) yhigh=y;	
   }
  }

xlow/=psize;
xhigh/=psize;
ylow/=Pitch;
yhigh/=Pitch;

UnLock();

Crop(xlow,ylow,xhigh,yhigh);
return Point(xlow,ylow);
}


void Image::Crop(int x1,int y1,int x2, int y2)
{
if(!IsData())  GENNFERR("(Crop)Image does not contain data");
if(x1<0) x1=0; if(x2<0) x2=0;
if(y1<0) y1=0; if(y2<0) y2=0;
if(x1>=xlen) x1=xlen-1; if(x2>=xlen) x2=xlen-1;
if(y1>=xlen) y1=ylen-1; if(y2>=ylen) y2=ylen-1;
if(x1>x2 || y1>y2)  GENNFERR("(Crop)Invalid Crop Dimensions");

int nxlen,nylen,x,y;
nxlen=x2-x1+1;
nylen=y2-y1+1;
int psize=(Bpp>>3);
int sx,sy,axl=nxlen*psize;
int ystop=nylen*nxlen*psize;

BYTE *dtp=Lock();
BYTE *ndt=new BYTE[nxlen*nylen*psize+4];  //+4 for overrun


for(y=0,sy=y1*Pitch;y<ystop;y+=nxlen*psize,sy+=Pitch)
 for(x=0,sx=x1*psize;x<axl;x++,sx++)
  ndt[x+y]=dtp[sx+sy];
  
UnLock();  
xlen=nxlen;
ylen=nylen;
xpos+=x1;
ypos+=y1;
IAlloc();
SetData(ndt);
delete ndt;
}

void Image::ReplaceAlpha(int acutoff,const ImgRGBColor &bcol)  //Replaces alpha <= this value with color
{
if(!IsData())  GENNFERR("(ReplaceAlpha)Image does not contain data");
if(Mask.GetABits()<0)  GENNFERR("(ReplaceAlpha)Image does not contain Alpha channel");

BYTE *dtp=Lock();

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
 
if(itype==ITYPE_RGB)
 {
 RGBPixel *tptr;
 ImgRGBColor col;	
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  if((signed int)col.a<=acutoff) tptr->Set(bcol,Mask);	 
      }
 }
else
{ //place 8 Bit implementation here
}

UnLock();
}

float Image::GetBrightness()
{
if(!IsData())  GENNFERR("(Swap)Image does not contain data");

BYTE *dtp=Lock();

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
float rsum,gsum,bsum;

rsum=gsum=bsum=0.0f; 

if(itype==ITYPE_RGB)
 {
 RGBPixel *tptr;
 ImgRGBColor col;	
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  rsum+=col.r;
	  gsum+=col.g;
	  bsum+=col.b;
      }
 }
else
{ //place 8 Bit implementation here
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  rsum+=pal[*(dtp+x+y)].r;
	  gsum+=pal[*(dtp+x+y)].g;
	  bsum+=pal[*(dtp+x+y)].b;
      }

}

UnLock();

return (gsum*0.59f+bsum*0.11f+rsum*0.3f)/(255*xlen*ylen);

}

void Image::BrightenBy(float bght)
{
if(!IsData())  GENNFERR("(Swap)Image does not contain data");

BYTE *dtp=Lock();

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
float rsum,gsum,bsum;

rsum=gsum=bsum=0.0f; 

if(itype==ITYPE_RGB)
 {
 RGBPixel *tptr;
 ImgRGBColor col;	
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  col.BrightenBy(bght);
	  tptr->Set(col,Mask);
      }
 }
else
{ //place 8 Bit implementation here
	for(int i=0;i<256;i++)
		pal[i].BrightenBy(bght);

}

UnLock();
}

void Image::ContrastBy(float cont)
{
if(!IsData())  GENNFERR("(Swap)Image does not contain data");

BYTE *dtp=Lock();

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
float rsum,gsum,bsum;

rsum=gsum=bsum=0.0f; 

if(itype==ITYPE_RGB)
 {
 RGBPixel *tptr;
 ImgRGBColor col;	
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  col.ContrastBy(cont);
	  tptr->Set(col,Mask);
      }
 }
else
{ //place 8 Bit implementation here
	for(int i=0;i<256;i++)
		pal[i].ContrastBy(cont);

}

UnLock();
}

void Image::FillHistogram(Histogram *hist,RMETHOD rmet)
{
if(!IsData())  GENNFERR("(Swap)Image does not contain data");
hist->Clear();
BYTE *dtp=Lock();

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
float rsum,gsum,bsum;

rsum=gsum=bsum=0.0f; 

if(itype==ITYPE_RGB)
 {
 RGBPixel *tptr;
 ImgRGBColor col;	
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  	
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  col.Bound();
		switch(rmet)
		{
		case RMD_REDC:
		hist->Data[col.r]++;
		break;
		case RMD_GREENC:
		hist->Data[col.g]++;
		break;
		case RMD_BLUEC:
		hist->Data[col.b]++;
		break;

		}
	  }
 }
else
{ //place 8 Bit implementation here

}
hist->Peak=GetWidth()*GetHeight();
UnLock();

}


void Image::FillHistogram(RGBHistogram *hist)
{
if(!IsData())  GENNFERR("(Swap)Image does not contain data");
hist->Clear();
BYTE *dtp=Lock();

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;

if(itype==ITYPE_RGB)
 {
 RGBPixel *tptr;
 ImgRGBColor col;	
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  	
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  col.Bound();
	  hist->RHist.Data[col.r]++;
	  hist->GHist.Data[col.g]++;
	  hist->BHist.Data[col.b]++;
		
	  }
 }
else
{ //place 8 Bit implementation here

}
hist->GHist.Peak=hist->BHist.Peak=hist->RHist.Peak=GetWidth()*GetHeight();
UnLock();

}

void Image::AutoAdjust()
{
RGBHistogram hist(*this);
AdjustToRange(hist.GetTop(),hist.GetBottom());
}

void Image::AdjustToRange(ImgRGBColor &maxcol,ImgRGBColor &mincol)
{

if(!IsData())  GENNFERR("(Swap)Image does not contain data");


int min=256,max=0,span=0;
min=mincol.r<min?mincol.r:min;
min=mincol.g<min?mincol.g:min;
min=mincol.b<min?mincol.b:min;

max=maxcol.r>max?maxcol.r:max;
max=maxcol.g>max?maxcol.g:max;
max=maxcol.b>max?maxcol.b:max;
span=max-min;


if(span==0) return;

BYTE *dtp=Lock();

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;

if(itype==ITYPE_RGB)
 {
 RGBPixel *tptr;
 ImgRGBColor col;	
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  col.r=((col.r-min)*256)/span;
	  col.g=((col.g-min)*256)/span;
	  col.b=((col.b-min)*256)/span;
	  col.Bound();
	  
	  tptr->Set(col,Mask);
      }
 }
else
{ //place 8 Bit implementation here
	for(int i=0;i<256;i++)

	{
	  pal[i].r=((pal[i].r-min)*256)/span;
	  pal[i].g=((pal[i].g-min)*256)/span;
	  pal[i].b=((pal[i].b-min)*256)/span;
		pal[i].Bound();
	}

}

UnLock();

}


void Image::Swap(const ImgRGBColor &src,const ImgRGBColor &dst)
{
if(!IsData())  GENNFERR("(Swap)Image does not contain data");

BYTE *dtp=Lock();

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
 
if(itype==ITYPE_RGB)
 {
 RGBPixel *tptr;
 ImgRGBColor col;	
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  if(col==src) tptr->Set(dst,Mask);
      }
 }
else
{ //place 8 Bit implementation here
}

UnLock();
}


void Image::MakeBW()
{
if(!IsData())  GENNFERR("(MakeBW)Image does not contain data");
BYTE *dtp=Lock();

 
if(itype==ITYPE_RGB)
 {
 int psize=(Bpp>>3);
 int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
 RGBPixel *tptr;
 ImgRGBColor col;	

	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  col.MakeBW();
	  tptr->Set(col,Mask);	 
	  }
	}
else
{ //place 8 Bit implementation here
AllocPal();
for(int i=0;i<256;i++) pal[i].MakeBW();
}

UnLock();
}

void Image::Invert()
{
if(!IsData())  GENNFERR("(Invert)Image does not contain data");
BYTE *dtp=Lock();
 
if(itype==ITYPE_RGB)
 {
 RGBPixel *tptr;
 ImgRGBColor col;	

	int psize=(Bpp>>3);
	int x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize) 
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  col.Invert();
	  tptr->Set(col,Mask);	 
	  }	

 }
else
{ //8 Bit implementation
AllocPal();
for(int i=0;i<256;i++) pal[i].Invert();
RealizePalette();
}

UnLock();
}

void Image::CreateBlank(int xl,int yl,ITYPE itp,int bpp,RGBMask &msk)
{
CreateBlank(xl,yl,itp,bpp);
Mask=msk;
}

void Image::CreateBlank(int xl,int yl,ITYPE itp,int bpp)
{
if(xl<0 || yl<0)  GENNFERR("(Create Fade) Invalid dimensions");
//if(bpp!=24 && bpp!=32 && bpp!=8)  GENNFERR("(Create Fade) Invalid depth");
if(itp!=ITYPE_RGB && itp!=ITYPE_INDEXED)  GENNFERR("(Create Fade) Invalid Image Type");
if(itp==ITYPE_RGB && bpp==8)      GENNFERR("(Create Fade) RGB Image cannot be 8-bit");
if(itp==ITYPE_INDEXED && bpp!=8)  GENNFERR("(Create Fade) Indexed Images must be 8-bit");

vxlen=xlen=xl; vylen=ylen=yl; itype=itp; Bpp=bpp;

if(Bpp==32)
	Mask=RGBMask(0x00ff0000,0x0000ff00,0x000000ff,0xff000000);
else
	Mask=RGBMask(0x00ff0000,0x0000ff00,0x000000ff);

IAlloc();

}

void Image::CreateFade(int xl,int yl,ImgRGBColor rgb1,ImgRGBColor rgb2,int ort,ITYPE itp,int bpp)
{
if(xl<0 || yl<0)  GENNFERR("(Create Fade) Invalid dimensions");
//if(bpp!=24 && bpp!=32 && bpp!=8)  GENNFERR("(Create Fade) Invalid depth");
if(itp!=ITYPE_RGB && itp!=ITYPE_INDEXED)  GENNFERR("(Create Fade) Invalid Image Type");
if(itp==ITYPE_RGB && bpp==8)      GENNFERR("(Create Fade) RGB Image cannot be 8-bit");
if(itp==ITYPE_INDEXED && bpp!=8)  GENNFERR("(Create Fade) Indexed Images must be 8-bit");

vxlen=xlen=xl; vylen=ylen=yl; itype=itp; Bpp=bpp;
int x,y,i;
double r,g,b,dist,rinc,ginc,binc;

IAlloc();
BYTE *dpt=Lock();

RGBPixel *tptr;
ImgRGBColor col;	

if(ort) dist=xlen; else dist=ylen;


int psize=(Bpp>>3);
int axl=xlen*psize;

switch(Bpp)
{
case 32: Mask=RGBMask(0x00ff0000,0x0000ff00,0x000000ff,0xff000000); break; 
case 24: Mask=RGBMask(0x00ff0000,0x0000ff00,0x000000ff,0x00000000); break; 
case 16: Mask=RGBMask(0x7c00,0x3e0,0x1f); break;

case 8:
						 //calculate pallette range
rinc=((float)rgb2.r-rgb1.r)/255;
ginc=((float)rgb2.g-rgb1.g)/255;
binc=((float)rgb2.b-rgb1.b)/255;
r=rgb1.r; g=rgb1.g; b=rgb1.b;
AllocPal();
for(i=0;i<256;i++) 
{
pal[i]=ImgRGBColor((int)r,(int)g,(int)b);
r+=rinc; g+=ginc; b+=binc;
}
RealizePalette();

for(y=0;y<ylen;y++)
 for(x=0;x<axl;x+=psize)
  dpt[x+y*Pitch]=(BYTE)(ort?(x*256)/xlen:(y*256)/ylen);
break;

}							 //calculate color range

if(Bpp>8)
{
rinc=((float)rgb2.r-rgb1.r)/dist;
ginc=((float)rgb2.g-rgb1.g)/dist;
binc=((float)rgb2.b-rgb1.b)/dist;
r=rgb1.r;
g=rgb1.g;
b=rgb1.b;

if(ort)
for(x=0;x<axl;x+=psize)
 {
 for(y=0;y<ylen;y++)
  {
  tptr=(RGBPixel *)(dpt+x+y*Pitch);
  col.Set((int)r,(int)g,(int)b);
  tptr->Set(col,Mask);	 
  }
 r+=rinc; g+=ginc; b+=binc;
 }
else 
for(y=0;y<ylen;y++)
 {
 for(x=0;x<axl;x+=psize)
  {
  tptr=(RGBPixel *)(dpt+x+y*Pitch);
  col.Set((int)r,(int)g,(int)b);
  tptr->Set(col,Mask);	 
  }
 r+=rinc; g+=ginc; b+=binc;
 }
}//end of cs

UnLock();
}

void Image::CreateSolid(int xl,int yl,ImgRGBColor col,ITYPE itp,int bpp)
{
if(xl<0 || yl<0)  GENNFERR("(Create Solid) Invalid dimensions");
//if(bpp!=24 && bpp!=32 && bpp!=8)  GENNFERR("(Create Solid) Invalid depth");
if (itp!=ITYPE_RGB && itp!=ITYPE_INDEXED)  GENNFERR("(Create Solid) Invalid Image Type");
if(itp==ITYPE_RGB && bpp==8)      GENNFERR("(Create Solid) RGB Image cannot be 8-bit");
if(itp==ITYPE_INDEXED && bpp!=8)  GENNFERR("(Create Solid) Indexed Images must be 8-bit");


vxlen=xlen=xl; vylen=ylen=yl; itype=itp; Bpp=bpp;

if(Bpp==32)
	Mask=RGBMask(0x00ff0000,0x0000ff00,0x000000ff,0xff000000);
else
	Mask=RGBMask(0x00ff0000,0x0000ff00,0x000000ff);

IAlloc();
FillColor(col);
}

void Image::FillColor(ImgRGBColor fcol)
{
int x,y,i;

BYTE *dpt=Lock();


int psize=(Bpp>>3);
int axl=xlen*psize;
RGBPixel tcl(fcol,Mask);
int ystop=ylen*Pitch;

switch(Bpp)
{
case 32:
case 24:
case 16:
for(y=0;y<ystop;y+=Pitch)
 for(x=0;x<axl;x+=psize)
  *((RGBPixel *)(dpt+x+y))=tcl;
break;

case 8:
AllocPal();
pal[0]=fcol;
RealizePalette();
for(i=0;i<ylen*Pitch;i++) dpt[i]=0;
break;
}

UnLock();
}


void Image::CreateGrid(int xdim,int ydim,int gx,int gy,ImgRGBColor bgk,ImgRGBColor frg,ITYPE itp,int bpp)
{
if(xdim<0 || ydim<0)  GENNFERR("(Create Grid) Invalid dimensions");
if(bpp!=24 && bpp!=32 && bpp!=8)  GENNFERR("(Create Grid) Invalid depth");
if (itp!=ITYPE_RGB && itp!=ITYPE_INDEXED)  GENNFERR("(Create Grid) Invalid Image Type");
if(itp==ITYPE_RGB && bpp==8)      GENNFERR("(Create Grid) RGB Image cannot be 8-bit");
if(itp==ITYPE_INDEXED && bpp!=8)  GENNFERR("(Create Grid) Indexed Images must be 8-bit");

vxlen=xlen=xdim; vylen=ylen=ydim;
itype=itp; Bpp=bpp;

int x,y,i;
IAlloc();
BYTE *dpt=Lock();
int psize=(Bpp>>3);
int axl=xlen*psize;
double xdiv=(xlen-1)/(float)gx,ydiv=(ylen-1)/(float)gy;
double xcnt,ycnt;
xcnt=ycnt=0.0;

RGBPixel fpx(frg,Mask);
RGBPixel bpx(bgk,Mask);

switch(Bpp)
{
case 32:
case 24:
case 16:

for(ycnt=0.0,y=0;y<ylen;y++,ycnt-=1.0)
 {
 if(ycnt<.5) 
  {
  for(x=0;x<axl;x+=psize)
    ((RGBPixel *)(dpt+x+y*Pitch))->Set(frg,Mask); 
  ycnt+=ydiv;
  }
 else
  for(xcnt=0.0,x=0;x<axl;x+=psize,xcnt-=1.0)
   if(xcnt<.5)
   { *((RGBPixel *)(dpt+x+y*Pitch))=fpx; xcnt+=xdiv;  }
    else
     *((RGBPixel *)(dpt+x+y*Pitch))=bpx;	 
 } 
break;

case 8:
AllocPal();
pal[0]=bgk;
pal[255]=frg;
RealizePalette();
for(i=0;i<ylen*Pitch;i++) dpt[i]=0;
for(ycnt=0.0,y=0;y<ylen;y++,ycnt-=1.0)
 {
 if(ycnt<.5) 
  { for(x=0;x<axl;x+=psize) dpt[x+y*Pitch]=255; ycnt+=ydiv; }
 else
  for(xcnt=0.0,x=0;x<axl;x+=psize,xcnt-=1.0)
   if(xcnt<.5) { dpt[x+y*Pitch]=255; xcnt+=xdiv;  }
 } 
break;
}//end of cs  


UnLock();
}  




void Image::Difference()
{
if(!IsData())  GENNFERR("(Difference)Image does not contain data");
BYTE *dt=Lock();
int byte=Bpp>>3;

int x,y;
int ystop=ylen*Pitch;
if(byte==3)
{
	for(y=0;y<ystop;y+=Pitch)
 for(x=xlen-1;x>=1;x--) 
  {
  dt[x*byte+y+0]-=dt[(x-1)*byte+y+0];
  dt[x*byte+y+1]-=dt[(x-1)*byte+y+1];
  dt[x*byte+y+2]-=dt[(x-1)*byte+y+2];
  }
}
else
if(byte==4)
{
	for(y=0;y<ystop;y+=Pitch)
 for(x=xlen-1;x>=1;x--) 
  {
  dt[x*byte+y+0]-=dt[(x-1)*byte+y+0];
  dt[x*byte+y+1]-=dt[(x-1)*byte+y+1];
  dt[x*byte+y+2]-=dt[(x-1)*byte+y+2];
  }
}
else
GENNFERR("(Difference)Image Size not supported");

 UnLock();
}

void Image::UnDifference()
{
if(!IsData())  GENNFERR("(UnDifference)Image does not contain data");
BYTE *dt=Lock();
int byte=Bpp>>3;

int x,y,b,xstop;
int ystop=ylen*Pitch;
xstop=xlen*byte;

for(y=0;y<ystop;y+=Pitch)
 for(x=byte;x<xstop;x+=byte) 
  for(b=0;b<byte;b++)
  {
  dt[x+y+b]+=dt[(x-byte)+y+b];
  }


UnLock();
}

void Image::MakeRGB16(RGBMask msk)
{
if(!IsData())  GENNFERR("(MakeRGB16)Image does not contain data");
if(Bpp==16)   return;
//if(Bpp==16)    GENNFERR("(MakeRGB16)Image is 16 bit");
int psize=(Bpp>>3);
int axl=xlen*psize;

BYTE *tndt,*ndt=new BYTE[xlen*ylen*2+4];  //+4 for overrun
tndt=ndt;
int x,y;
BYTE *dt=Lock();

switch(itype)
{
 case ITYPE_INDEXED: 
  for(y=0;y<ylen;y++) 
   for(x=0;x<xlen;x++,tndt+=2) 
    ((RGBPixel *)tndt)->Set(pal[dt[x+y*Pitch]],msk);
 break;

 case ITYPE_RGB: 
 {
 ImgRGBColor col; 
 RGBPixel *tptr;
int ystop=ylen*Pitch;
 for(y=0;y<ystop;y+=Pitch)
  for(x=0;x<axl;x+=psize,tndt+=2)
  {
  tptr=(RGBPixel *)(dt+x+y);
  col.Set(*tptr,Mask);
  ((RGBPixel *)tndt)->Set(col,msk);
  }
 }
 break;
}

Mask=msk;
Bpp=16;
UnLock();
itype=ITYPE_RGB; 
IAlloc();
SetData(ndt);
delete ndt;


}

void Image::MakeRGB(RGBMask msk)
{
if(!IsData())  GENNFERR("(MakeRGB)Image does not contain data");

if(itype==ITYPE_INDEXED) MakeRGB24();

int opsize,npsize,x,y,axl;
opsize=Bpp>>3;
npsize=msk.GetBpp()>>3;
axl=xlen*opsize;



BYTE *tndt,*ndt;
ndt=new unsigned char[xlen*ylen*npsize+4];  //+4 for overrun
if(ndt==NULL) GENFERR("MemoryAllocation Error");
tndt=ndt;
BYTE *dt=Lock();

ImgRGBColor col; 
RGBPixel *tptr;

int ystop=ylen*Pitch;
 for(y=0;y<ystop;y+=Pitch)
  for(x=0;x<axl;x+=opsize,tndt+=npsize)
  {
  tptr=(RGBPixel *)(dt+x+y);
  col.Set(*tptr,Mask);
  ((RGBPixel *)tndt)->Set(col,msk);
  }


UnLock();
Mask=msk;
Bpp=msk.GetBpp();
itype=ITYPE_RGB; 
IAlloc();
SetData(ndt);
delete ndt;
}

void Image::MakeRGB24()
{
if(!IsData())  GENNFERR("(MakeRGB24)Image does not contain data");
//if(Bpp==24)  GENNFERR("(MakeRGB24)Image is 24 bit");

if(itype==ITYPE_INDEXED)
{
BYTE *tndt,*ndt=new BYTE[xlen*ylen*3+4];  //+4 for overrun
tndt=ndt;
int x,y;
BYTE *dt=Lock();

int ystop=ylen*Pitch;
for(y=0;y<ystop;y+=Pitch)
 for(x=0;x<xlen;x++,tndt+=3) 
  ((RGBPixel *)tndt)->Set(pal[dt[x+y]],Mask);

UnLock();

itype=ITYPE_RGB; 
Bpp=24;
IAlloc();
SetData(ndt);
delete ndt;
}

if(itype==ITYPE_RGB) MakeRGB(RGBMask(0xff0000,0xff00,0xff,0x00));

}


void Image::CorrectPitch()  //corrects the pitch of loaded image
{
int axl=xlen*(Bpp>>3);
if(Pitch==axl) return;
if(!IsData())  GENNFERR("(CorrectPitch)Image does not contain data");

int x,y;
BYTE *data=Lock();

for(y=ylen-1;y>=0;y--)
 for(x=axl-1;x>=0;x--)
  data[x+Pitch*y]=data[x+axl*y];
  
UnLock();
}

void Image::DePitch(BYTE *ndt)  //return depicthed COPY of the data, must be deleted	
{
BYTE *dat;
int axl=xlen*(Bpp>>3);
dat=Lock();
int x,y;

for(y=0;y<ylen;y++)
 for(x=0;x<axl;x++)
  ndt[x+axl*y]=dat[x+y*Pitch];

UnLock();
}	

void Image::MaskTo(RGBMask &imask)  //Remask the data to this mask to correct mask
{
if(!IsData())  GENNFERR("(MaskTo)Image does not contain data");
if(itype!=ITYPE_RGB)  GENNFERR("(MaskTo)Cannot Mask a non RGB Image");
if(!imask.IsValid())  GENNFERR("(MaskTo)Invalid Mask");
if(imask==Mask) return;

BYTE *dtp=Lock();
RGBPixel *tptr;
int psize=(Bpp>>3);
int x,y,axl=xlen*psize;

ImgRGBColor col; 
int ystop=ylen*Pitch;
for(y=0;y<ystop;y+=Pitch)
  for(x=0;x<axl;x+=psize)
  {
  tptr=(RGBPixel *)(dtp+x+y);
  col.Set(*tptr,Mask);
  tptr->Set(col,imask);
  }
Mask=imask;
UnLock();


}


void Image::MaskFrom(RGBMask &omask)  //Remask the data from this mask to correct mask
{
if(!IsData())  GENNFERR("(MaskFrom)Image does not contain data");
if(itype!=ITYPE_RGB)  GENNFERR("(MaskFrom)Cannot Mask a non RGB Image");
if(!omask.IsValid())  GENNFERR("(MaskFrom)Invalid Mask");
if(omask==Mask) return;

BYTE *dtp=Lock();
RGBPixel *tptr;
int psize=(Bpp>>3);
int x,y,axl=xlen*psize;

ImgRGBColor col;	
int ystop=ylen*Pitch;
for(y=0;y<ystop;y+=Pitch)
  for(x=0;x<axl;x+=psize)
  {
  tptr=(RGBPixel *)(dtp+x+y);
  col.Set(*tptr,omask);
  tptr->Set(col,Mask);
  }

UnLock();
}


void Image::DeInterlace()   //deinterlaces a 4 interlaced image
{
if(!IsData())  GENNFERR("(Deinterlace)Image does not contain data");
BYTE *dtp=Lock();

int psize=(Bpp>>3);
int x,y,axl=xlen*psize;
BYTE *tndt,*ndt=new BYTE[xlen*ylen*psize+4];  //+4 for overrun
tndt=ndt;
 
if(itype==ITYPE_RGB)
  switch(Bpp)
  {
  case 24:
  case 32:
  break;

  case 16:
  break;
  } //end of cs  
else
{ //place 8 Bit implementation here
int pass,skip,start,tln=0;

for(pass=0;pass<4;pass++)
  {
  if(pass) {skip=16/(1<<pass); start=skip>>1; }
   else {skip=8; start=0;}

  for(y=start;y<ylen;y+=skip,tln++)
   for(x=0;x<axl;x+=psize)  ndt[x+y*ylen]=dtp[x+tln*ylen]; 
  }


}

SetData(ndt);
delete ndt;
UnLock();
}

void  Image::Quantize()
{
if(!IsData())  GENNFERR("(Quantize)Image does not contain data");
if(Bpp<16)  GENNFERR("(Quantize)Not Enough Colors");

OctQuant tree;
tree.Quantize(this);

}

void Image::ReduceTo8Bit(RMETHOD rm)
{
if(!IsData())  GENNFERR("(MakeBW)Image does not contain data");
if(Bpp<=8)  GENNFERR("(ReduceTo8Bit)Already 8bit");
BYTE *dtp=Lock();

BYTE *newdata;

newdata=new BYTE[GetWidth()*GetHeight()];

 
if(itype==ITYPE_RGB)
 {
 int psize=(Bpp>>3);
 int inc=0,x,y,axl=xlen*psize;
int ystop=ylen*Pitch;
 RGBPixel *tptr;
 ImgRGBColor col;	

switch(rm)
	{
	case RMD_REDC:
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  newdata[inc++]=(BYTE)col.r;
	  tptr->Set(col,Mask);	 
	  }
	break;

	case RMD_BLUEC:
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  newdata[inc++]=(BYTE)col.b;
	  tptr->Set(col,Mask);	 
	  }
	break;

	case RMD_GREENC:
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  newdata[inc++]=(BYTE)col.g;
	  tptr->Set(col,Mask);	 
	  }
	break;

	case RMD_ALPHAC:
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  newdata[inc++]=(BYTE)col.a;
	  tptr->Set(col,Mask);	 
	  }
	break;

	case RMD_MAX:
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  newdata[inc++]=(BYTE)IMax(IMax(IMax(col.r,col.g),col.b),col.a); 
	  tptr->Set(col,Mask);	 
	  }
	break;

	case RMD_BHT:	
	 for(y=0;y<ystop;y+=Pitch)
	  for(x=0;x<axl;x+=psize)
	  {
	  tptr=(RGBPixel *)(dtp+x+y);
	  col.Set(*tptr,Mask);
	  col.MakeBW();
	  newdata[inc++]=(BYTE)col.r;
	  tptr->Set(col,Mask);	 
	  }
	break;
	}

 }

UnLock();

Bpp=8;
itype=ITYPE_INDEXED;
IAlloc();
SetData(newdata);
MakeGrayscalePalette();
RealizePalette();
delete newdata;


}

void
	Image::CreateDifferentialMap()
{
	if(!IsData())
		GENNFERR("(MakeBW)Image does not contain data");

	if(Bpp<=8)
		GENNFERR("(ReduceTo8Bit)Already 8bit");

	BYTE *dtp=Lock();

	if(itype==ITYPE_RGB)
	{
		int psize=(Bpp>>3);
		int x, y, axl = (xlen-1)*psize;
		int ystop = (ylen-1)*Pitch;
		RGBPixel *tptr0, *tptr1, *tptr2;
		ImgRGBColor col0, col1, col2;	

		for(y=0;y<ystop;y+=Pitch)
			for(x=0;x<axl;x+=psize)
			{
				tptr0 = (RGBPixel *)(dtp+x+y);
				col0.Set(*tptr0,Mask);

				tptr1 = (RGBPixel *)(dtp+(x+psize)+y);
				col1.Set(*tptr1,Mask);

				tptr2 = (RGBPixel *)(dtp+x+(y+Pitch));
				col2.Set(*tptr2,Mask);

				col0.g = (BYTE)abs((int)(((col1.r-col0.r)+(col2.r-col0.r))*0.25f*32));

				tptr0->Set(col0, Mask);	 
			}
		for(y=0;y<ystop;y+=Pitch)
			for(x=0;x<axl;x+=psize)
			{
				tptr0 = (RGBPixel *)(dtp+x+y);
				col0.Set(*tptr0,Mask);

				tptr1 = (RGBPixel *)(dtp+(x+psize)+y);
				col1.Set(*tptr1,Mask);

				tptr2 = (RGBPixel *)(dtp+x+(y+Pitch));
				col2.Set(*tptr2,Mask);

				col0.b = (BYTE)abs((int)(((col1.g-col0.g)+(col2.g-col0.g))*0.25f*4));

		 		tptr0->Set(col0, Mask);	 
			}
	}

	UnLock();
}

void
	Image::CreateDifferentialMap(float *im, int xdim)
{
	if(!IsData())
		GENNFERR("(MakeBW)Image does not contain data");

	if(Bpp<=8)
		GENNFERR("(ReduceTo8Bit)Already 8bit");

	BYTE *dtp=Lock();

	if(itype==ITYPE_RGB)
	{
		int psize=(Bpp>>3);
		int i, j, x, y, axl = (xlen-1)*psize;
		int ystop = (ylen-1)*Pitch;
		RGBPixel *tptr0, *tptr1, *tptr2;
		ImgRGBColor col0, col1, col2;	

		for(i=0,y=0;y<ystop;i++,y+=Pitch)
			for(j=0,x=0;x<axl;j++,x+=psize)
			{
				tptr0 = (RGBPixel *)(dtp+x+y);

				col0.r = static_cast<BYTE>(im[i*xdim+j]);
				col0.g = static_cast<BYTE>(abs((int)(((im[i*xdim+j+1]-im[i*xdim+j])+(im[(i+1)*xdim+j]-im[i*xdim+j]))*0.25f*32)));

				tptr0->Set(col0, Mask);	 
			}
		for(y=0;y<ystop;y+=Pitch)
			for(x=0;x<axl;x+=psize)
			{
				tptr0 = (RGBPixel *)(dtp+x+y);
				col0.Set(*tptr0,Mask);

				tptr1 = (RGBPixel *)(dtp+(x+psize)+y);
				col1.Set(*tptr1,Mask);

				tptr2 = (RGBPixel *)(dtp+x+(y+Pitch));
				col2.Set(*tptr2,Mask);

				col0.b = static_cast<BYTE>(abs((int)(((col1.g-col0.g)+(col2.g-col0.g))*0.25f*4)));

		 		tptr0->Set(col0, Mask);	 
			}
	}

	UnLock();
}

bool Image::operator==(Image &img)
{
if(itype!=img.itype || Bpp!=img.Bpp || xlen!=img.xlen || ylen!=img.ylen) return false;

if(itype!=ITYPE_INDEXED)
	MaskTo(img.Mask);

BYTE *ptr1,*ptr2;
ptr1=(BYTE *)Lock();
ptr2=(BYTE *)img.Lock();

int y,axl=xlen*(Bpp>>3);
for(y=0;y<ylen;y++)
{
if(memcmp(ptr1+y*Pitch,ptr2+y*img.Pitch,axl)!=0) return false;
}


UnLock();
img.UnLock();
return true;
}


Image &Image::operator=(Image &img)
{
int i;
Flag=img.Flag;
if(img.pal)
{
AllocPal();
for(i=0;i<256;i++) pal[i]=img.pal[i];
}
strcpy(fname,img.fname);
Mask=img.Mask;
Bpp=img.Bpp;
Pitch=img.Pitch;
xlen=img.xlen;
ylen=img.ylen;
itype=img.itype;
xpos=img.xpos;
ypos=img.ypos;
vxlen=img.vxlen;
vylen=img.vylen;
IAlloc();
SetData(img.Data);
return *this;
}

void Image::Pad(int pixs)
{
	BYTE *SDat=Lock();

	int x, y, sx, sy, nxlen=xlen+pixs*2, nylen=ylen+pixs*2;
	int psize=(Bpp>>3);

	BYTE *DDat=new BYTE[nxlen*nylen*psize];

	int b;

	for(y=-pixs;y<ylen+pixs;y++)
	{
		for(x=-pixs;x<xlen+pixs;x++)
		{
			if(x<0)
			{
				sx = 0;
			}
			else
			{
				if(x>=xlen)
				{
					sx = xlen-1;
				}
				else
				{
					sx = x;
				}
			}

			if(y<0)
			{
				sy=0;
			}
			else
			{
				if(y>=ylen)
				{
					sy = ylen-1;
				}
				else
				{
					sy = y;
				}
			}
		
			for(b=0;b<psize;b++)
			{
				DDat[psize*((x+pixs)+(y+pixs)*nxlen) + b] = SDat[psize*(sx+sy*xlen) + b];
			}
		}
	}


	delete Data;
	Data=DDat;
	xlen=nxlen;
	ylen=nylen;
	Pitch=xlen*(Bpp>>3);

	UnLock();
}

void Image::MakeSlopeMap(float xscale,float yscale,float zscale,ImgRGBColor lowcol,ImgRGBColor highcol)
{
	if(!IsData())
		GENNFERR("(MakeSlopeMap)Image does not contain data");

	if(Bpp>8) ReduceTo8Bit(RMD_MAX);

	BYTE *dtp=Lock();

	int psize=(Bpp>>3);
	int x, y, axl = (xlen-1)*psize;
	int ystop = (ylen-1)*Pitch;
	float gdist,tdist,ascale;
	int cp;

	ascale=(float)sqrt(xscale*xscale+yscale*yscale);
	for(y=Pitch;y<ystop-Pitch;y+=Pitch)
			for(x=1;x<axl-1;x+=psize)
			{
			cp=(int)dtp[x+y];
			gdist=(float)atan(fabs((dtp[x+y-1]-cp)*zscale/xscale));
			tdist=(float)atan(fabs((cp-dtp[x+y+1])*zscale/xscale));
			gdist=tdist>gdist?tdist:gdist;
			tdist=(float)atan(fabs((dtp[x+y-Pitch]-cp)*zscale/yscale));
			gdist=tdist>gdist?tdist:gdist;
			tdist=(float)atan(fabs((cp-dtp[x+y+Pitch])*zscale/yscale));
			gdist=tdist>gdist?tdist:gdist;
/*
			tdist=(float)atan(fabs((dtp[x+y-(Pitch+1)]-cp)*zscale/ascale));
			gdist=tdist>gdist?tdist:gdist;
			tdist=(float)atan(fabs((cp-dtp[x+y+(Pitch+1)])*zscale/ascale));
			gdist=tdist>gdist?tdist:gdist;
			tdist=(float)atan(fabs((dtp[x+(y-Pitch)+1]-cp)*zscale/ascale));
			gdist=tdist>gdist?tdist:gdist;
			tdist=(float)atan(fabs((cp-dtp[x+y+Pitch+1])*zscale/ascale));
			gdist=tdist>gdist?tdist:gdist;
*/			
			dtp[x+y-(Pitch+1)]=(BYTE)(gdist*256.0f/3.14159265359f);

			}

MakePalette(lowcol,highcol);

UnLock();
}

void Image::MakeContourMap(float xscale,float yscale,float zscale,
						   int step,
						   ImgRGBColor bgcol,
						   ImgRGBColor lolinecol,ImgRGBColor highlinecol)
{
	if(!IsData())
		GENNFERR("(MakeSlopeMap)Image does not contain data");

	if(Bpp>8) ReduceTo8Bit(RMD_MAX);
	if(xlen<4 || ylen<4) return;

	BYTE *dtp=Lock();

	int psize=(Bpp>>3);
	int x, y, axl = (xlen-1)*psize;
	int ystop = (ylen-1)*Pitch;
	int cp;
	int blim=256/step;
	for(y=0;y<ystop;y+=Pitch)
		for(x=0;x<axl;x+=psize)
			dtp[x+y]=(dtp[x+y]/blim)*blim;

	for(y=Pitch;y<ystop-Pitch;y+=Pitch)
	{
		for(x=1;x<axl-1;x+=psize)
			{
			cp=(int)dtp[x+y];
			if( 
				dtp[x+y-1]!=cp ||
				dtp[x+y+1]!=cp ||
				dtp[x+y-Pitch]!=cp ||
				dtp[x+y+Pitch]!=cp ||
				dtp[(x+y-Pitch)-1]!=cp ||
				dtp[(x+y-Pitch)+1]!=cp ||
				dtp[(x+y+Pitch)-1]!=cp ||
				dtp[(x+y+Pitch)+1]!=cp 
				)
				dtp[x+y-(Pitch+1)]=cp;
			else
				dtp[x+y-(Pitch+1)]=0;
			}
	}
MakePalette(lolinecol,highlinecol);
pal[0]=bgcol;

UnLock();
}

void Image::ShowSlopeLimit(float xscale,float yscale,float zscale,float limit,ImgRGBColor lowcol,ImgRGBColor highcol)
{
	if(!IsData())
		GENNFERR("(MakeSlopeMap)Image does not contain data");

	if(Bpp>8) ReduceTo8Bit(RMD_MAX);

	BYTE *dtp=Lock();

	int psize=(Bpp>>3);
	int x, y, axl = (xlen-1)*psize;
	int ystop = (ylen-1)*Pitch;
	float gdist,tdist,ascale;
	int cp;

	ascale=(float)sqrt(xscale*xscale+yscale*yscale);
	for(y=Pitch;y<ystop-Pitch;y+=Pitch)
			for(x=1;x<axl-1;x+=psize)
			{
			cp=(int)dtp[x+y];
			gdist=(float)atan(fabs((dtp[x+y-1]-cp)*zscale/xscale));
			tdist=(float)atan(fabs((cp-dtp[x+y+1])*zscale/xscale));
			gdist=tdist>gdist?tdist:gdist;
			tdist=(float)atan(fabs((dtp[x+y-Pitch]-cp)*zscale/yscale));
			gdist=tdist>gdist?tdist:gdist;
			tdist=(float)atan(fabs((cp-dtp[x+y+Pitch])*zscale/yscale));
			gdist=tdist>gdist?tdist:gdist;

			gdist=gdist*(180.0f/3.14159265359f);
				dtp[x+y-(Pitch+1)]=(BYTE)(gdist*256.0f/3.14159265359f);

			if(gdist>limit)
				dtp[x+y-(Pitch+1)]=255;
			else
				dtp[x+y-(Pitch+1)]=0;
			}

MakePalette(lowcol,highcol);

UnLock();
}

void Image::MakePalette(ImgRGBColor low,ImgRGBColor high)
{

	AllocPal();
	int i;
	
	float cr,cg,cb;
	float dr,dg,db;
	cr=(float)low.r;
	cg=(float)low.g;
	cb=(float)low.b;
	
	dr=(high.r-low.r)/256.0f;
	dg=(high.g-low.g)/256.0f;
	db=(high.b-low.b)/256.0f;


	for(i=0;i<256;i++)
	{
		pal[i]=ImgRGBColor((int)cr,(int)cg,(int)cb);
		cr+=dr;
		cb+=db;
		cg+=dg;
	}

RealizePalette();
}
