#include"image.h"
#include<string.h>

int Image::IsTiff(FILE *fl)
{
unsigned short int tmps;

if(!fl)  GENNFERR("(Is Tiff)Invalid File");
int fpos=ftell(fl);

fread(&tmps,2,1,fl);    // read data type
if(tmps==0x4949 || tmps==0x4d4d)
 {
 fread(&tmps,2,1,fl);    // read data type
 fseek(fl,fpos,SEEK_SET);
 if(tmps==0x2a)  return 1;  
 }
fseek(fl,fpos,SEEK_SET);

return 0;
}

void Image::LoadTiff(char *str)
{
FILE *fl=fopen(str,"rb"); 

if(!fl) 
{
char tfn[MAX_PATH*2];
strcpy(tfn,"Cannont open TIF file for reading:");
strcat(tfn,str);
 GENNFERR(tfn);
}

LoadTiff(fl);
fclose(fl);
strcpy(fname,str);
}

void Image::SaveTiff(char *str)
{
FILE *fl=fopen(str,"wb"); 

if(!fl) 
{
char tfn[200];
strcpy(tfn,"Cannont open TIF file for Writeing:");
strcat(tfn,str);
 GENNFERR(tfn);
}

SaveTiff(fl);
fclose(fl);
strcpy(fname,str);
}


//>>>>>>>>Tiff
void Image::LoadTiff(FILE *fl)
{
unsigned char *odata,*cdata;
long int *sloc,*bps;
long int i,initpos,fpos,tmpl,comp=999;
long int strip,rps,samples;
short int abts,rbts,gbts,bbts,tmps,nof,nos;
long unsigned int *codetable;
unsigned char *FileBuf;
int dflag=0;
abts=0;
initpos=ftell(fl);                 //get file position

fread(&tmps,2,1,fl);    // read data type
switch(tmps)
 {
 case 0x4949: break;             //LSB
 case 0x4D4D:  GENNFERR("Tiff Error-Mac Images unsupported"); break;   //MSB
 default:   GENNFERR("Tiff Error-Undefined Byte ordering");
 }

fread(&tmps,2,1,fl);    // read version number
if(tmps!=0x2a)  GENNFERR("Tiff Error-Wrong Version");        //wrong version

fread(&tmpl,4,1,fl);            // read offset to directory
fseek(fl,tmpl+initpos,SEEK_SET);   // setposition

fread(&nof,2,1,fl);             // read number of fieldss

for(i=0;i<nof;i++)
{
struct { short int tag,type; long int lenght,offset;} field;
fread(&field,12,1,fl);             // read number of field

fpos=ftell(fl);          //get file position
switch(field.tag)
 {
 case 254:  //File Subtype
 break;
 
 case 256: vxlen=xlen=field.offset; break; //Get xsize
 case 257: vylen=ylen=field.offset; break; //Get ysize
 
 case 258:   //get bits per sample
  fseek(fl,initpos+field.offset,SEEK_SET);        // setposition
   fread(&rbts,2,1,fl);            // red bits
   fread(&gbts,2,1,fl);            // green bits
   fread(&bbts,2,1,fl);            // blue bits
 if(field.lenght>3)
   fread(&abts,2,1,fl);            // alpha bits ABCD :)
 break;
 
 case 259:    //get compression scheme
 comp=field.offset;
 if(comp!=1 && comp!=5)  GENNFERR("Tiff Error-Unsupported Compression");   //for uncompressed
 break;

 case 262:                          //get photometric interp
 if(field.offset!=2)   GENNFERR("Tiff Error-Image Not RGB"); // not RGB
 break;
 
 case 273:                         //get striplocation
 nos=(short int)field.lenght;
 sloc=new long int[nos];
 if(nos<=1)  sloc[0]=(short unsigned int)field.offset; 
 else
 {
  fseek(fl,initpos+field.offset,SEEK_SET);        // setposition
  if(field.type==3)  fread(sloc,2*nos,1,fl);  
   else if(field.type==4) fread(sloc,4*nos,1,fl);
    else  GENNFERR("Tiff Error");

 }
 break;

 case 277:                      //samples per pixel
 samples=field.offset;
 switch(samples)
  {
  case 4:
  case 3:
  case 2:
  itype=ITYPE_RGB;
  Bpp=8*samples;
  break;

  default:
   GENNFERR("Tiff Error-Invalid Pixel Format");
 }
 break;
 
 case 278: rps=field.offset; break;  //rowsperstrip
 
 case 279: 
 bps=new long int[nos];
 if(nos<=1)  bps[0]=(short unsigned int)field.offset; 
  else
 {
 fseek(fl,initpos+field.offset,SEEK_SET);        // setposition
 fread(bps,nos*4,1,fl);
 }
 break;                    //bytesperstrip
 
 case 282: break;                    //pixels per xres
 case 283: break;                    //pixels per yres
 
 case 284:                     //sampleplanes
 if(field.offset!=1)  GENNFERR("Tiff Error-Not Single Plane");       //not single planer
 break;

 case 296: break;                    //resolution unit
 
 case 317:                           //predictor
 dflag=field.offset; 
 if(dflag!=1 && dflag!=2)  GENNFERR("Tiff Error-Invalid Diff");      //uses differtiation
 break;

 } //end of switch
fseek(fl,fpos,SEEK_SET);        // resetposition
} //end of number of fields 

if(abts)   Mask.Set(0x000000ff,0x0000ff00,0x00ff0000,0xff000000);
 else  Mask.Set(0x000000ff,0x0000ff00,0x00ff0000,0) ;

IAlloc();
cdata=Lock();
odata=cdata;

//begin decompression

codetable=new DWORD[4096*4];

if(comp==5)
 for(strip=0;strip<nos;strip++)
 {
 int endflag=1,bitlen=9,totalbits=0,newcode;
 int bytecount,currentcode=258;
 register DWORD li;
 DWORD offset=0,databuf=0,mask; 
 bytecount=rps*(Bpp>>3)*xlen;

 fseek(fl,initpos+sloc[strip],SEEK_SET);   //goto strip
 FileBuf= new unsigned char[bps[strip]];
 fread(FileBuf,bps[strip],1,fl); 
 int fbufcnt=0;

 mask=(1<<bitlen)-1;
  while(endflag && bytecount>0)
  {           //do lzw
   while(totalbits<bitlen)   //get more data
    { 
      databuf=(databuf<<8)+(DWORD)FileBuf[fbufcnt++]; totalbits+=8; }
   
   
   newcode=(databuf>>(totalbits-bitlen))&mask;     //get the code
   totalbits-=bitlen;

   if (newcode==257) endflag=0;
    else          // end code 
    if (newcode==256) {bitlen=9; currentcode=257; mask=(1<<bitlen)-1;}  // clearcode 
     else
    if (newcode<256)
     {
     cdata[offset]=(unsigned char)newcode; 
     codetable[currentcode]=offset++;
     codetable[currentcode+1]=offset;
     bytecount--;
     }
     else
     {
     if (newcode>currentcode)   GENNFERR("Tiff Decompression Error");          //check for bad code
     codetable[currentcode]=offset;
     
     for(li=codetable[newcode];li<=codetable[newcode+1];li++)
      cdata[offset++]=cdata[li];
     bytecount-=codetable[newcode+1]-codetable[newcode]+1;

     codetable[currentcode+1]=offset;

     }  //end of else >=256
  
  if(((DWORD)++currentcode)==(DWORD)1<<bitlen && bitlen<12) 
   { bitlen++; mask=(1<<bitlen)-1; }
  } //end of while endflag
 cdata+=offset;
 delete FileBuf; 
 } //end of strip loop
else
if(comp==1)
 for(strip=0;strip<nos;strip++)
 {
 fseek(fl,initpos+sloc[strip],SEEK_SET);   //goto strip
 fread(cdata+strip*rps*xlen*(Bpp>>3),bps[strip],1,fl);
 }

CorrectPitch();					//adjust data for pitch.
if(dflag==2) UnDifference();


UnLock();
delete codetable;
delete bps;
delete sloc;
}  //end of load Tiff Member

//>>>>>>>>>>>>>>>>>> File Writers

//>>>>>>>>Tiff

void Image::SaveTiff(FILE *fl)
{
int i,initpos,pos,cpos,tmpl,btotal,samples;
int strip,bps,slc,byps,nos,res,lps,bpstrip;
int *sloc,*bpsl;
unsigned char *cpydata;
WORD tmps;
short int *nptable;
DWORD *codetable;
struct { short int tag,type; long int lenght,offset;} field;
  int ep,fof,evl,ocd,cnt; 
  unsigned char *ptmp,*p2;
  int *iptmp,*ip2;

//fname=fl.GetFileName();
if(Mask.GetAMask()) MaskTo(RGBMask(0x0000ff,0x00ff00,0xff0000,0xff000000)); 
 else MaskTo(RGBMask(0x0000ff,0x00ff00,0xff0000)); 
if(!IsData())  GENNFERR("(SaveTiff)Image does not contain data");

BYTE *tmpbuf;
if(Pitch==xlen*(Bpp>>3)) cpydata=Lock();
 else DePitch(cpydata=tmpbuf=new BYTE[xlen*ylen*(Bpp>>3)]);


initpos=ftell(fl);

samples=Bpp>>3;
/*
switch(Bpp)
{
case 2: samples=2; break;
case 3: samples=3; break;
default : return 12;
}
*/
lps=0x4000/(xlen*samples);
bpstrip=lps*xlen*samples;
nos=ylen/lps;
if(ylen%lps) nos++;
btotal=xlen*ylen*samples;

fwrite("II",2,1,fl);  //write lsb code
tmps=0x2a; fwrite(&tmps,2,1,fl);            // write version number
tmpl=8;    fwrite(&tmpl,4,1,fl);            // write offset to directory
tmps=15;   fwrite(&tmps,2,1,fl);             // write number of fieldss

field.tag=254; field.type=4; field.lenght=1; field.offset=0;
fwrite(&field,12,1,fl);  //write file code

field.tag=256; field.type=4; field.lenght=1; field.offset=xlen;
fwrite(&field,12,1,fl);  //write file xlen

field.tag=257; field.type=4; field.lenght=1; field.offset=ylen;
fwrite(&field,12,1,fl);  //write file ylen

bps=ftell(fl);
field.tag=258; field.type=3; field.lenght=3; field.offset=999;
fwrite(&field,12,1,fl);  //write bit per sample

field.tag=259; field.type=3; field.lenght=1; field.offset=5;
fwrite(&field,12,1,fl);  //write compression scheme(LZW)

field.tag=262; field.type=3; field.lenght=1; field.offset=2;
fwrite(&field,12,1,fl);  //write photometric RGB

slc=ftell(fl);
field.tag=273; field.type=4; field.lenght=nos; field.offset=999;
fwrite(&field,12,1,fl);  //write striplocation


field.tag=277; field.type=3; field.lenght=1; field.offset=samples;
fwrite(&field,12,1,fl);  //write samples perpixel

field.tag=278; field.type=4; field.lenght=1; field.offset=lps;
fwrite(&field,12,1,fl);  //write rows per strip

byps=ftell(fl);
field.tag=279; field.type=4; field.lenght=nos; field.offset=999;
fwrite(&field,12,1,fl);  //write bytes per strip

res=ftell(fl);
field.tag=282; field.type=5; field.lenght=1; field.offset=999;
fwrite(&field,12,1,fl);  //write planer

field.tag=283; field.type=5; field.lenght=1; field.offset=999;
fwrite(&field,12,1,fl);  //write xres

field.tag=284; field.type=3; field.lenght=1; field.offset=1;
fwrite(&field,12,1,fl);  //write yres

field.tag=296; field.type=3; field.lenght=1; field.offset=2;
fwrite(&field,12,1,fl);  //write resolution

field.tag=317; field.type=3; field.lenght=1; field.offset=1;
fwrite(&field,12,1,fl);  //write predictor

tmpl=0;
fwrite(&tmpl,4,1,fl); // write bits persample

pos=ftell(fl);
samples=Bpp>>3; 
tmps=(WORD)Mask.GetRBits(); fwrite(&tmps,2,1,fl); // write red bits persample
tmps=(WORD)Mask.GetGBits(); fwrite(&tmps,2,1,fl); // write green bits persample
tmps=(WORD)Mask.GetBBits(); fwrite(&tmps,2,1,fl); // write blue bits persample
if(Mask.GetAMask()) 
{tmps=(WORD)Mask.GetABits(); fwrite(&tmps,2,1,fl); }// write alpha bits persample

cpos=ftell(fl);
fseek(fl,bps,SEEK_SET);        // setposition
field.tag=258; field.type=3; field.lenght=3; field.offset=pos-initpos;
if(Mask.GetAMask()) field.lenght++;
fwrite(&field,12,1,fl);  //write bit per sample
fseek(fl,cpos,SEEK_SET);        // resetposition

pos=ftell(fl);
tmpl=576; fwrite(&tmpl,4,1,fl); 
tmpl=5; fwrite(&tmpl,4,1,fl); 
tmpl=72; fwrite(&tmpl,4,1,fl); 
tmpl=1; fwrite(&tmpl,4,1,fl); 
cpos=ftell(fl);

fseek(fl,res,SEEK_SET);        // setposition
field.tag=282; field.type=5; field.lenght=1; field.offset=pos-initpos;
fwrite(&field,12,1,fl);  //xres
field.tag=283; field.type=5; field.lenght=1; field.offset+=8;
fwrite(&field,12,1,fl);  //yres
fseek(fl,cpos,SEEK_SET);        // resetposition

sloc=new int[nos];
bpsl=new int[nos];
nptable=new short int[4096]; 
codetable=new DWORD[4096]; 

for(strip=0;strip<nos;strip++)
{
DWORD databuf=256,offset=0;
int bytecount,totalbits=9,bitlen=9,currentcode=258;
int endflag=1,match=0;
unsigned char tbyte;

bytecount=bpstrip; 

if(bytecount>btotal) bytecount=btotal;
else btotal-=bytecount;

sloc[strip]=ftell(fl)-initpos;

for(i=0;i<4096;i++) nptable[i]=-1;

while (endflag)
 {
 while(totalbits>=8) { tbyte=(unsigned char)(databuf>>(totalbits-8))&0xff; fwrite(&tbyte,1,1,fl);  totalbits-=8;}
 codetable[currentcode]=offset;
  
  ptmp=cpydata+offset;
  iptmp=(int *)ptmp;
  match=0;
 ocd=nptable[*ptmp]; // get the starting reference code
 while(ocd!=-1 && !match)  // if there is a prior code and no match
  {
  fof=codetable[ocd];
  evl=(codetable[ocd+1]-fof)+1;
  p2=cpydata+fof;
  ip2=(int *)p2;
  
  if (evl<=bytecount)
   {
   ep=evl>>2;                                          //dword method
   for(cnt=0;cnt<ep && ip2[cnt]==iptmp[cnt];cnt++); //compare dwords
   for(cnt<<=2;cnt<evl && p2[cnt]==ptmp[cnt];cnt++); //compare end bytes
   }
  
  if(cnt==evl && cnt<=bytecount)
   {
   match=1; //set the match
   databuf=(databuf<<bitlen)|(DWORD)(ocd); 
   totalbits+=bitlen; //add the code
   offset+=cnt;                             //increment the count
   bytecount-=cnt;
   codetable[currentcode+1]=offset;     //set the offset
   nptable[currentcode]=nptable[*ptmp];      //record last current code
   nptable[*ptmp]=currentcode;              //record most recent code
   }
  else
  ocd=nptable[ocd];                   //reference next code
  } //end of ocd while
 
 if(!match) 
  {
  databuf=(databuf<<bitlen)|((DWORD)cpydata[offset]); totalbits+=bitlen;
  offset++;
  codetable[currentcode+1]=offset;     //set the offset
  bytecount--;
  nptable[currentcode]=nptable[*ptmp];      //record last current code
  nptable[*ptmp]=currentcode;              //record most recent code
  }

 if(currentcode>=0xffe)      //send clear code
  {
  databuf=(databuf<<bitlen)|((DWORD)256); 
  totalbits+=bitlen;
  bitlen=9;
  currentcode=257;
  for(i=0;i<4096;i++) nptable[i]=-1;
  }

 
 if(bytecount<0)  GENNFERR("(SaveTiff)Bad Byte Count");
					 
 if(bytecount==0) 
  {
  endflag=0;    //endflag
  while(totalbits>=8) {tbyte=(unsigned char)(databuf>>(totalbits-8))&0xff; fwrite(&tbyte,1,1,fl); totalbits-=8;}
  databuf=(databuf<<bitlen)|((DWORD)257); totalbits+=bitlen; //send end code
  while(totalbits>=8) {tbyte=(unsigned char)(databuf>>(totalbits-8))&0xff; fwrite(&tbyte,1,1,fl); totalbits-=8;}
  tbyte=(unsigned char)databuf<<(8-totalbits);
  fwrite(&tbyte,1,1,fl);
  }
 
 if(((DWORD)++currentcode)==(DWORD)1<<bitlen)  bitlen++;
 
 } //end of while
cpydata+=offset;
bpsl[strip]=(ftell(fl)-initpos)-sloc[strip];
} //end of strip

pos=ftell(fl);
for(i=0;i<nos;i++) fwrite(sloc+i,4,1,fl); // striplocations
cpos=ftell(fl);
fseek(fl,slc,SEEK_SET);        // setposition
field.tag=273; field.type=4; field.lenght=nos; field.offset=pos-initpos;
fwrite(&field,12,1,fl);  //write striplocation
fseek(fl,cpos,SEEK_SET);        // resetposition

if(nos>1)
 {
 pos=ftell(fl);
 for(i=0;i<nos;i++) fwrite(sloc+i,4,1,fl); // striplocations
 cpos=ftell(fl);
 fseek(fl,slc,SEEK_SET);        // setposition
 field.tag=273; field.type=4; field.lenght=nos; field.offset=pos-initpos;
 fwrite(&field,12,1,fl);  //write striplocation
 fseek(fl,cpos,SEEK_SET);        // resetposition
 
 
 pos=ftell(fl);
 for(i=0;i<nos;i++) fwrite(bpsl+i,4,1,fl); // byte per strip
 cpos=ftell(fl);
 fseek(fl,byps,SEEK_SET);        // setposition
 field.tag=279; field.type=4; field.lenght=nos; field.offset=pos-initpos;
 fwrite(&field,12,1,fl);  //write bytes per strip
 fseek(fl,cpos,SEEK_SET);        // resetposition
 }
else
 {
 cpos=ftell(fl);
 fseek(fl,slc,SEEK_SET);        // setposition
 field.tag=273; field.type=4; field.lenght=nos; field.offset=sloc[0];
 fwrite(&field,12,1,fl);  //write striplocation
 fseek(fl,cpos,SEEK_SET);        // resetposition
 
 
 cpos=ftell(fl);
 fseek(fl,byps,SEEK_SET);        // setposition
 field.tag=279; field.type=4; field.lenght=nos; field.offset=bpsl[0];
 fwrite(&field,12,1,fl);  //write bytes per strip
 fseek(fl,cpos,SEEK_SET);        // resetposition
 }


if(Pitch==(xlen*(Bpp>>3))) UnLock();
 else delete tmpbuf;
MaskFrom(RGBMask(0x0000ff,0x00ff00,0xff0000)); 

delete nptable;
delete codetable;
delete sloc;
delete bpsl;
}
