#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "paulslib.h"
#include "bitmaplib.h"

//#define TGA_READ_USE_FREAD TRUE

/*
   Create a bitmap structure
*/
BITMAP4 *Create_Bitmap(int nx,int ny)
{
   return((BITMAP4 *)malloc(nx*ny*sizeof(BITMAP4)));
}

/*
   Destroy the bitmap structure
*/
void Destroy_Bitmap(BITMAP4 *bm)
{
   free(bm);
}

/*
	Compare two pixels
*/
int Same_BitmapPixel(BITMAP4 p1,BITMAP4 p2)
{
	if (p1.r != p2.r) return(FALSE);
   if (p1.g != p2.g) return(FALSE);
   if (p1.b != p2.b) return(FALSE);
   if (p1.a != p2.a) return(FALSE);
	return(TRUE);
}

/*
   Write a bitmap to a file
	The format is as follows
     1 == tga 
    11 == tga with alpha
    12 == compressed tga
    13 == compressed tga with alpha
     2 == ppm
	  3 == rgb
	  4 == raw grey scale
     5 == tiff
	  6 == EPS colour (Encapsulated PostScript)
	  7 == EPS black and white
 	  8 == raw
     9 == BMP
	A negative format indicates a vertical flip
*/
void Write_Bitmap(FILE *fptr,BITMAP4 *bm,int nx,int ny,int format)
{
   int i,j,offset;
	long index,rowindex;
	int linelength = 0,size;
	char buffer[1024];

	/* Write the header */
	switch (ABS(format)) {
	case 1:
	case 11:
	case 12:
	case 13:
		putc(0,fptr);  /* Length of ID */
		putc(0,fptr);  /* No colour map */
		if (ABS(format) == 12 || ABS(format) == 13) 
			putc(10,fptr); /* compressed RGB */
		else
			putc(2,fptr); /* uncompressed RGB  */ 
		putc(0,fptr); /* Index of colour map entry */
		putc(0,fptr);
		putc(0,fptr); /* Colour map length */
		putc(0,fptr);
		putc(0,fptr); /* Colour map size */
		putc(0,fptr); /* X origin */
		putc(0,fptr);
		putc(0,fptr); /* Y origin */
		putc(0,fptr);
   	putc((nx & 0x00ff),fptr); /* X width */
   	putc((nx & 0xff00) / 256,fptr);
   	putc((ny & 0x00ff),fptr); /* Y width */
   	putc((ny & 0xff00) / 256,fptr);
		if (ABS(format) == 11 || ABS(format) == 13) {
         putc(32,fptr);                      /* 32 bit bitmap     */
			putc(0x08,fptr);
		} else {
			putc(24,fptr);                 		/* 24 bit bitmap 		*/
			putc(0x00,fptr);
		}
		break;
	case 2:
		fprintf(fptr,"P6\n%d %d\n255\n",nx,ny);
		break;
	case 3:
		putc(0x01,fptr);
		putc(0xda,fptr);
		putc(0x00,fptr);
		putc(0x01,fptr);
		putc(0x00,fptr);
		putc(0x03,fptr);
		putc((nx & 0xFF00) / 256,fptr);
		putc((nx & 0x00FF),fptr);
		putc((ny & 0xFF00) / 256,fptr);
      putc((ny & 0x00FF),fptr);
		BM_WriteHexString(fptr,"000300000000000000ff00000000");
		fprintf(fptr,"WriteBitmap, pdb");
      putc(0x00,fptr);
      putc(0x00,fptr);
      putc(0x00,fptr);
      putc(0x00,fptr);
      putc(0x00,fptr);
      putc(0x00,fptr);
      putc(0x00,fptr);
      putc(0x00,fptr);
		break;
	case 4:
		break;
	case 5:
		BM_WriteHexString(fptr,"4d4d002a");	/* Little endian & TIFF identifier */
		offset = nx * ny * 3 + 8;
		BM_WriteLongInt(fptr,buffer,offset);
		break;
	case 6:
		fprintf(fptr,"%%!PS-Adobe-3.0 EPSF-3.0\n");
		fprintf(fptr,"%%%%Creator: Created from bitmaplib by Paul Bourke\n");
		fprintf(fptr,"%%%%BoundingBox: %d %d %d %d\n",0,0,nx,ny);
		fprintf(fptr,"%%%%LanguageLevel: 2\n");
		fprintf(fptr,"%%%%Pages: 1\n");
		fprintf(fptr,"%%%%DocumentData: Clean7Bit\n");
		fprintf(fptr,"%d %d scale\n",nx,ny);
		fprintf(fptr,"%d %d 8 [%d 0 0 -%d 0 %d]\n",nx,ny,nx,ny,ny);
      fprintf(fptr,"{currentfile 3 %d mul string readhexstring pop} bind\n",nx);
      fprintf(fptr,"false 3 colorimage\n");
		break;
   case 7:
      fprintf(fptr,"%%!PS-Adobe-3.0 EPSF-3.0\n");
      fprintf(fptr,"%%%%Creator: Created from bitmaplib by Paul Bourke\n");
      fprintf(fptr,"%%%%BoundingBox: %d %d %d %d\n",0,0,nx,ny);
      fprintf(fptr,"%%%%LanguageLevel: 2\n");
      fprintf(fptr,"%%%%Pages: 1\n");
      fprintf(fptr,"%%%%DocumentData: Clean7Bit\n");
      fprintf(fptr,"%d %d scale\n",nx,ny);
      fprintf(fptr,"%d %d 8 [%d 0 0 -%d 0 %d]\n",nx,ny,nx,ny,ny);
      fprintf(fptr,"{currentfile %d string readhexstring pop} bind\n",nx);
      fprintf(fptr,"false 1 colorimage\n");
      break;
	case 8:
		break;
	case 9:
		/* Header 10 bytes */
		putc('B',fptr);
		putc('M',fptr);
		size = nx * ny * 3 + 14 + 40;
		putc((size) % 256,fptr);
		putc((size / 256) % 256,fptr);
		putc((size / 65536) % 256,fptr);
		putc((size / 16777216),fptr);
		putc(0,fptr); putc(0,fptr); 
		putc(0,fptr); putc(0,fptr);
		/* Offset to image data */
		putc(14+40,fptr); putc(0,fptr); putc(0,fptr); putc(0,fptr); 
		/* Information header 40 bytes */
		putc(0x28,fptr); putc(0,fptr); putc(0,fptr); putc(0,fptr); 
      putc((nx) % 256,fptr);
      putc((nx / 256) % 256,fptr);
      putc((nx / 65536) % 256,fptr);
      putc((nx / 16777216),fptr);
      putc((ny) % 256,fptr);
      putc((ny / 256) % 256,fptr);
      putc((ny / 65536) % 256,fptr);
      putc((ny / 16777216),fptr);
		putc(1,fptr); putc(0,fptr); /* One plane */
		putc(24,fptr); putc(0,fptr); /* 24 bits */
		/* Compression type == 0 */
		putc(0,fptr); putc(0,fptr); putc(0,fptr); putc(0,fptr); 
		size = nx * ny * 3;
      putc((size) % 256,fptr);
      putc((size / 256) % 256,fptr);
      putc((size / 65536) % 256,fptr);
      putc((size / 16777216),fptr);
      putc(1,fptr); putc(0,fptr); putc(0,fptr); putc(0,fptr); 
      putc(1,fptr); putc(0,fptr); putc(0,fptr); putc(0,fptr); 
      putc(0,fptr); putc(0,fptr); putc(0,fptr); putc(0,fptr); /* No palette */
      putc(0,fptr); putc(0,fptr); putc(0,fptr); putc(0,fptr); 
		break;
	}

	/* Write the binary data */
   for (j=0;j<ny;j++) {
		if (format > 0)
			rowindex = j * nx;
		else
			rowindex = (ny - 1 - j) * nx;
		switch (ABS(format)) {
	   case 12:
         WriteTGACompressedRow(fptr,&(bm[rowindex]),nx,3);
         break;
      case 13:
         WriteTGACompressedRow(fptr,&(bm[rowindex]),nx,4);
         break;
		}	
      for (i=0;i<nx;i++) {
			if (format > 0) 
         	index = rowindex + i;
			else
				index = rowindex + i;
			switch (ABS(format)) {
			case 1:
			case 11:
			case 9:
            putc(bm[index].b,fptr);
            putc(bm[index].g,fptr);
            putc(bm[index].r,fptr);
				if (ABS(format) == 11)
					putc(bm[index].a,fptr);
            break;
			case 2:
			case 3:
			case 5:
			case 8:
            putc(bm[index].r,fptr);
            putc(bm[index].g,fptr);
            putc(bm[index].b,fptr);
				break;
			case 4:
				putc((bm[index].r+bm[index].g+bm[index].b)/3,fptr);
				break;
			case 6:
				fprintf(fptr,"%02x%02x%02x",bm[index].r,bm[index].g,bm[index].b);
				linelength += 6;
				if (linelength >= 72 || linelength >= nx) {
					fprintf(fptr,"\n");
					linelength = 0;
				}	
				break;
         case 7:
            fprintf(fptr,"%02x",(bm[index].r+bm[index].g+bm[index].b)/3);
            linelength += 2;
            if (linelength >= 72 || linelength >= nx) {
               fprintf(fptr,"\n");
               linelength = 0;
            } 
            break;
			}
      }
   }

	/* Write the footer */
	switch (ABS(format)) {
	case 1:
	case 11:
	case 12:
	case 13:
	case 2:
	case 3:
	case 4:
		break;
	case 5:
		putc(0x00,fptr); /* The number of directory entries (14) */
      putc(0x0e,fptr);

		/* Width tag, short int */
		BM_WriteHexString(fptr,"0100000300000001");
		putc((nx & 0xff00) / 256,fptr);		/* Image width */
		putc((nx & 0x00ff),fptr);
		putc(0x00,fptr);
		putc(0x00,fptr);

		/* Height tag, short int */
		BM_WriteHexString(fptr,"0101000300000001");
      putc((ny & 0xff00) / 256,fptr);    /* Image height */
      putc((ny & 0x00ff),fptr);
		putc(0x00,fptr);
		putc(0x00,fptr);

		/* bits per sample tag, short int */
		BM_WriteHexString(fptr,"0102000300000003");
		offset = nx * ny * 3 + 182;
		BM_WriteLongInt(fptr,buffer,offset);

		/* Compression flag, short int */
		BM_WriteHexString(fptr,"010300030000000100010000");

		/* Photometric interpolation tag, short int */
		BM_WriteHexString(fptr,"010600030000000100020000");

		/* Strip offset tag, long int */
		BM_WriteHexString(fptr,"011100040000000100000008");

		/* Orientation flag, short int */
		BM_WriteHexString(fptr,"011200030000000100010000");

		/* Sample per pixel tag, short int */
		BM_WriteHexString(fptr,"011500030000000100030000");

		/* Rows per strip tag, short int */
		BM_WriteHexString(fptr,"0116000300000001");
      putc((ny & 0xff00) / 256,fptr); 
      putc((ny & 0x00ff),fptr);
		putc(0x00,fptr);
		putc(0x00,fptr);

		/* Strip byte count flag, long int */
		BM_WriteHexString(fptr,"0117000400000001");
      offset = nx * ny * 3;
		BM_WriteLongInt(fptr,buffer,offset);

		/* Minimum sample value flag, short int */
		BM_WriteHexString(fptr,"0118000300000003");
      offset = nx * ny * 3 + 188;
		BM_WriteLongInt(fptr,buffer,offset);

		/* Maximum sample value tag, short int */
		BM_WriteHexString(fptr,"0119000300000003");
      offset = nx * ny * 3 + 194;
		BM_WriteLongInt(fptr,buffer,offset);

		/* Planar configuration tag, short int */
		BM_WriteHexString(fptr,"011c00030000000100010000");

		/* Sample format tag, short int */
		BM_WriteHexString(fptr,"0153000300000003");
      offset = nx * ny * 3 + 200;
		BM_WriteLongInt(fptr,buffer,offset);

		/* End of the directory entry */
		BM_WriteHexString(fptr,"00000000");

		/* Bits for each colour channel */
		BM_WriteHexString(fptr,"000800080008");

		/* Minimum value for each component */
		BM_WriteHexString(fptr,"000000000000");

		/* Maximum value per channel */
		BM_WriteHexString(fptr,"00ff00ff00ff");

		/* Samples per pixel for each channel */
		BM_WriteHexString(fptr,"000100010001");

		break;
	case 6:
	case 7:
		fprintf(fptr,"\n%%%%EOF\n");
		break;
   case 8:
   case 9:
		break;
	}
}

/*
	Write a compressed TGA row
	Depth is either 3 or 4
*/
void WriteTGACompressedRow(FILE *fptr,BITMAP4 *bm,int width,int depth)
{
	int i;
	int counter = 1;
	int pixelstart = 0;
	int packettype = 0;
	int readytowrite = FALSE;
	BITMAP4 currentpixel,nextpixel = {0,0,0,0};

	currentpixel = bm[0];
	for (;;) {
      if (pixelstart+counter >= width)  // Added April to fix strange bug
         readytowrite = TRUE;
      else
         nextpixel = bm[pixelstart+counter];

		if (!readytowrite) {
			if (Same_BitmapPixel(currentpixel,nextpixel)) {
				if (packettype == 0) {
					counter++;
					if (counter >= 128 || (pixelstart + counter) >= width) 
						readytowrite = TRUE;
				} else {
					counter--;
					readytowrite = TRUE;
				}
			} else {
				if (packettype == 1 || counter <= 1) {
					packettype = 1;
					currentpixel = nextpixel;
					counter++;
					if (counter >= 128 || (pixelstart + counter) >= width)
						readytowrite = TRUE;
				} else {
					readytowrite = TRUE;
				}
			}
		}

		if (readytowrite) {
			if (pixelstart + counter > width)
				counter = width - pixelstart;
			if (packettype == 0) {
				putc(((counter-1) | 0x80),fptr);
            putc(currentpixel.b,fptr);
            putc(currentpixel.g,fptr);
            putc(currentpixel.r,fptr);
            if (depth == 4)
               putc(currentpixel.a,fptr);
				currentpixel = nextpixel;
			} else {
				putc(counter-1,fptr);
				for (i=0;i<counter;i++) {
					putc(bm[pixelstart+i].b,fptr);
            	putc(bm[pixelstart+i].g,fptr);
            	putc(bm[pixelstart+i].r,fptr);
            	if (depth == 4)
               	putc(bm[pixelstart+i].a,fptr);
				}
			}
			if ((pixelstart = pixelstart + counter) >= width)
            break; /* From for (;;) */
			readytowrite = FALSE;
			packettype = 0;
			counter = 1;
		}
	}
}

void BM_WriteLongInt(FILE *fptr,char *s,long n)
{
	int i;

	s[0] = (n & 0xff000000) / 16777216;
	s[1] = (n & 0x00ff0000) / 65536;
	s[2] = (n & 0x0000ff00) / 256;
	s[3] = (n & 0x000000ff);

	for (i=0;i<4;i++)
		putc(s[i],fptr);
}

void BM_WriteHexString(FILE *fptr,char *s)
{
	unsigned int i;
	int c;
	char hex[3];

	for (i=0;i<strlen(s);i+=2) {
      hex[0] = s[i];
      hex[1] = s[i+1];
      hex[2] = '\0';
      sscanf(hex,"%X",&c);
		putc(c,fptr);
	}
}

/*
   Clear the bitmap to a particular colour
*/
void Erase_Bitmap(BITMAP4 *bm, int nx, int ny, BITMAP4 col)
{
   int i,j;
	long index;

   for (i=0;i<nx;i++) {
      for (j=0;j<ny;j++) {
         index = j * nx + i;
         bm[index] = col;
      }
   }
}

/*
	Scale an image using bicubic interpolation
*/
void BiCubicScale(
   BITMAP4 *bm_in,int nx,int ny,
   BITMAP4 *bm_out,int nnx,int nny)
{
   int i_out,j_out,i_in,j_in,ii,jj;
   int n,m;
	long index;
   double cx,cy,dx,dy,weight;
   double red,green,blue,alpha;
   BITMAP4 col;
 
   for (i_out=0;i_out<nnx;i_out++) {
      for (j_out=0;j_out<nny;j_out++) {
         i_in = (i_out * nx) / nnx;
         j_in = (j_out * ny) / nny;
         cx = i_out * nx / (double)nnx;
         cy = j_out * ny / (double)nny;
         dx = cx - i_in;
         dy = cy - j_in;
         red   = 0;
         green = 0;
         blue  = 0;
			alpha = 0;
         for (m=-1;m<=2;m++) {
            for (n=-1;n<=2;n++) {
               ii = i_in + m;
               jj = j_in + n;
               if (ii < 0)   ii = 0;
               if (ii >= nx) ii = nx-1;
               if (jj < 0)   jj = 0;
               if (jj >= ny) jj = ny-1;
               index = jj * nx + ii;
               weight = BiCubicR(m-dx) * BiCubicR(n-dy);
					// weight = BiCubicR(m-dx) * BiCubicR(dy-n);
               red   += weight * bm_in[index].r;
               green += weight * bm_in[index].g;
               blue  += weight * bm_in[index].b;
					alpha += weight * bm_in[index].a;
            }
         }
         col.r = (int)red;
         col.g = (int)green;
         col.b = (int)blue;
			col.a = (int)alpha;
         bm_out[j_out * nnx + i_out] = col;
      }
   }
}

double BiCubicR(double x)
{
   double xp2,xp1,xm1;
   double r = 0;

   xp2 = x + 2;
   xp1 = x + 1;
   xm1 = x - 1;

   if (xp2 > 0)
      r += xp2 * xp2 * xp2;
   if (xp1 > 0)
      r -= 4 * xp1 * xp1 * xp1;
   if (x > 0)
      r += 6 * x * x * x;
   if (xm1 > 0)
      r -= 4 * xm1 * xm1 * xm1;

   return(r / 6.0);
}

/*
	Scale a bitmap
	Apply a gaussian radial average if r > 0
	r is in units of the input image
*/
void GaussianScale(
	BITMAP4 *bm_in,int nx,int ny,
	BITMAP4 *bm_out,int nnx,int nny,double r)
{
	int i,j,ii,jj,ci,cj;
	long index;
	double x,y,cx,cy,red,green,blue,alpha,dist2,r2,weight,sum;
	BITMAP4 col,black = {0,0,0,255};

	r2 = r*r;

	for (i=0;i<nnx;i++) {
		for (j=0;j<nny;j++) {
			col = black;
			if (r2 <= 0) {
         	ci = (i * nx) / nnx;
         	cj = (j * ny) / nny;
				index = cj * nx + ci;
				col = bm_in[index];
			} else {
            cx = i * nx / (double)nnx;
            cy = j * ny / (double)nny;
				red   = 0;
				green = 0;
				blue  = 0;
				alpha = 0;
				sum = 0;
				for (x=cx-4*r;x<=cx+4*r+0.01;x++) {
					for (y=cy-4*r;y<=cy+4*r+0.01;y++) {
						ii = (int)x;
						jj = (int)y;
						if (ii < 0)
							ii = 0;
						if (ii >= nx)
							ii = nx-1;;
						if (jj < 0)
							jj = 0;
						if (jj >= ny) 
							jj = ny-1;
						dist2 = (cx-x)*(cx-x) + (cy-y)*(cy-y);
						weight = exp(-0.5*dist2/r2) / (r2*TWOPI);
						index = jj * nx + ii;
						red   += weight * bm_in[index].r;
						green += weight * bm_in[index].g;
						blue  += weight * bm_in[index].b;
						alpha += weight * bm_in[index].a;
						sum += weight;
					}
				}
				col.r = (int)red;
            col.g = (int)green;
            col.b = (int)blue;
				col.a = (int)alpha;
			}
			bm_out[j * nnx + i] = col;
		}
	}
}

/*
   Turn on a pixel of a bitmap
*/
int Draw_Pixel(BITMAP4 *bm, int nx, int ny, int x, int y, BITMAP4 col)
{
   long index;

   if (x < 0 || y < 0 || x >= nx || y >= ny)
      return(FALSE);
   index = y * nx + x;
   bm[index] = col;
	return(TRUE);
}

/*
   Return the value of a pixel
*/
BITMAP4 Get_Pixel(BITMAP4 *bm, int nx, int ny, int x, int y)
{
   long index;
	BITMAP4 black = {0,0,0,255};

	if (x < 0 || y < 0 || x >= nx || y >= ny)
		return(black);
   index = y * nx + x;
   return(bm[index]);
}

/*
   Draw a line from (x1,y1) to (x2,y2)
   Use colour col
*/
void Draw_Line(BITMAP4 *bm,int nx,int ny,int x1,int y1,int x2,int y2,BITMAP4 col)
{
   int i,j;
	long index;
   double mu,dx,dy,dxy;

   dx = x2 - x1;
   dy = y2 - y1;
   dxy = sqrt(dx*dx + dy*dy);
	if (dxy <= 0) {
		Draw_Pixel(bm,nx,ny,x1,y1,col); 
		return;
	}
   for (mu=0;mu<=2*dxy;mu++) {
      i = (int)(x1 + 0.5 * mu * dx / dxy);
      j = (int)(y1 + 0.5 * mu * dy / dxy);
		if (i < 0 || j < 0 || i >= nx || j >= ny)
			continue;
      index = j * nx + i;
      bm[index] = col;
   }
}

/*
	Scale a RGB value, dealing with clipping issues
*/
BITMAP4 Scale_Pixel(BITMAP4 pixelin,double scale)
{
	BITMAP4 pixelout;
	double r,g,b,a=0;

	r = pixelin.r * scale;
   g = pixelin.g * scale;
   b = pixelin.b * scale;
	a = pixelin.a * scale;
	
	if (r < 000) r = 0;
	if (r > 255) r = 255;
   if (g < 000) g = 0;
   if (g > 255) g = 255;
   if (b < 000) b = 0;
   if (b > 255) b = 255;
   if (a < 000) a = 0;
   if (a > 255) a = 255;

	pixelout.r = (int)r;
	pixelout.g = (int)g;
	pixelout.b = (int)b;
	pixelout.a = (int)a;

	return(pixelout);
}

/*
	Flip an image about an axis
	mode == 0 for horizontal
	mode == 1 for vertical
	This library assumes the (0,0) coordinate is top left
*/
void Flip_Bitmap(BITMAP4 *image,int width,int height,int mode)
{
	int i,j;
	long index1,index2;
	BITMAP4 p;

	switch (mode) {
	case 0:
		for (j=0;j<height/2;j++) {
			for (i=0;i<width;i++) {
				index1 = j * width + i;
				index2 = (height-1-j) * width + i;
				p = image[index1];
				image[index1] = image[index2];
				image[index2] = p;
			}
		}
		break;
	case 1:
      for (j=0;j<height;j++) {
         for (i=0;i<width/2;i++) {
            index1 = j * width + i;
            index2 = j * width + (width-1-i);
            p = image[index1];
            image[index1] = image[index2];
            image[index2] = p;
         }
      }
		break;
	}
}

/*
   Get the size and depth of a TGA file
*/
void TGA_Info(FILE *fptr,int *width,int *height,int *depth)
{
	int lo,hi;
   TGAHEADER header;

   header.idlength = fgetc(fptr);
   header.colourmaptype = fgetc(fptr);
   header.datatypecode = fgetc(fptr);
   fread(&header.colourmaporigin,2,1,fptr);
   fread(&header.colourmaplength,2,1,fptr);
   header.colourmapdepth = fgetc(fptr);
   fread(&header.x_origin,2,1,fptr);
   fread(&header.y_origin,2,1,fptr);
   lo = fgetc(fptr);
   hi = fgetc(fptr);
   header.width = hi*256 + lo;
   lo = fgetc(fptr);
   hi = fgetc(fptr);
   header.height = hi*256 + lo;
   header.bitsperpixel = fgetc(fptr);
	header.imagedescriptor = fgetc(fptr);

	*width  = header.width;
	*height = header.height;
	*depth  = header.bitsperpixel;

	rewind(fptr);
}

/*
	Read the TGA image data
	Return 0 on success
	Error codes
		1 - Failed to get legal colour type code
		2 - Failed to get legal bits per pixel
		3 - Failed to get legal colour map type
		4 - Failed to read colour data
		5 - Failed to read colour table
*/
int TGA_Read(FILE *fptr,BITMAP4 *image,int *width,int *height)
{
	int n=0,i,j;
	int lo,hi,index;
	int bytes2read,skipover = 0;  
	TGAHEADER header;
	unsigned char p[5];
	BITMAP4 *ctable = NULL;

#ifdef TGA_READ_USE_FREAD
	int k;
   int buffptr = 0;
   unsigned char *buffer = NULL;
   int buffread = 0;
#endif

   /* Read the header */
   header.idlength = fgetc(fptr);
   header.colourmaptype = fgetc(fptr);
   header.datatypecode = fgetc(fptr);
   lo = fgetc(fptr);
   hi = fgetc(fptr);
   header.colourmaporigin = hi*256 + lo;
   lo = fgetc(fptr);
   hi = fgetc(fptr);
   header.colourmaplength = hi*256 + lo;
   header.colourmapdepth = fgetc(fptr);
   fread(&header.x_origin,2,1,fptr);
   fread(&header.y_origin,2,1,fptr);
	lo = fgetc(fptr);
	hi = fgetc(fptr);
	header.width = hi*256 + lo;
	*width = header.width;
   lo = fgetc(fptr);
   hi = fgetc(fptr);
	header.height = hi*256 + lo;
	*height = header.height;
   header.bitsperpixel = fgetc(fptr);
   header.imagedescriptor = fgetc(fptr);
	
	/* 
		Can only handle image type 1, 2, 3 and 10 
		 1 - index colour uncompressed
		 2 - rgb uncompressed
		10 - rgb rle comrpessed
		 3 - grey scale uncompressed
       9 - rle index colour (unsupported)
		11 - rle black and white
	*/ 
   if (header.datatypecode != 1 &&
		 header.datatypecode != 2 && 
		 header.datatypecode != 3 &&
       header.datatypecode != 11 &&
		 header.datatypecode != 10) {
      return(1);
   }

	/* Can only handle pixel depths of 8, 16, 24, and 32 */
   if (header.bitsperpixel != 8 &&
		 header.bitsperpixel != 16 &&
       header.bitsperpixel != 24 && 
		 header.bitsperpixel != 32) {
      return(2);
   }

	/* 
		Can only handle colour map types of 0 and 1
		Ignore the colour map case (1) for RGB images!
	*/
   if (header.colourmaptype != 0 && header.colourmaptype != 1) {
      return(3);
   }

	/* Read the colour index table */
	if (header.datatypecode == 1) {
		ctable = (BITMAP4 *)malloc(header.colourmaplength*sizeof(BITMAP4));
		bytes2read = header.colourmapdepth / 8;
		for (i=0;i<header.colourmaplength;i++) {
         if ((int)fread(p,1,bytes2read,fptr) != bytes2read) 
            return(5);
         TGA_MergeBytes(&(ctable[i]),p,bytes2read);
		}
	} 

   /* Go to the start of the image data */
   skipover = 18;
   skipover += header.idlength;
   skipover += header.colourmaptype * header.colourmaplength * header.colourmapdepth / 8;
   fseek(fptr,skipover,SEEK_SET);

#ifdef TGA_READ_USE_FREAD
   /* Read the image */
   buffer = malloc(header.width * header.height * 4); // maximum size
   buffread = fread(buffer,1,header.width * header.height * 4,fptr);
   fprintf(stderr,"Read %d bytes\n",buffread);
#endif

   /* Read the image */
   bytes2read = header.bitsperpixel / 8;
   while (n < header.width * header.height) {
		if (header.datatypecode == 1) {                     /* Indexed uncompressed */
#ifdef TGA_READ_USE_FREAD
         index = buffer[buffptr++];
#else
         if ((index = fgetc(fptr)) == EOF) 
            return(4);
#endif
			if (index < 0) 
				index = 0;
			if (index >= header.colourmaplength)
				index = header.colourmaplength-1;
			image[n] = ctable[index];
			n++;
      } else if (header.datatypecode == 2) {              /* RGB Uncompressed */
#ifdef TGA_READ_USE_FREAD
         for (k=0;k<bytes2read;k++)
            p[k] = buffer[buffptr++];
#else
         if ((int)fread(p,1,bytes2read,fptr) != bytes2read)
            return(4);
#endif
         TGA_MergeBytes(&(image[n]),p,bytes2read);
         n++;
      } else if (header.datatypecode == 3) {              /* Grey Uncompressed */
#ifdef TGA_READ_USE_FREAD
         for (k=0;k<bytes2read;k++)
            p[k] = buffer[buffptr++];
#else
         if ((int)fread(p,1,bytes2read,fptr) != bytes2read) 
            return(4);
#endif
         TGA_MergeBytes(&(image[n]),p,bytes2read);
         n++;
      } else if (header.datatypecode == 10) {             /* RGB Compressed */
#ifdef TGA_READ_USE_FREAD
         for (k=0;k<bytes2read+1;k++)
            p[k] = buffer[buffptr++];
#else
         if ((int)fread(p,1,bytes2read+1,fptr) != bytes2read+1) 
            return(4);
#endif
         j = p[0] & 0x7f;
         TGA_MergeBytes(&(image[n]),&(p[1]),bytes2read);
         n++;
         if (p[0] & 0x80) {         /* RLE chunk */
            for (i=0;i<j;i++) {
               TGA_MergeBytes(&(image[n]),&(p[1]),bytes2read);
               n++;
            }
         } else {                   /* Normal chunk */
            for (i=0;i<j;i++) {
               if ((int)fread(p,1,bytes2read,fptr) != bytes2read) 
                  return(6);
               TGA_MergeBytes(&(image[n]),p,bytes2read);
               n++;
            }
         }
      } else if (header.datatypecode == 11) {             /* Compressed black and white */
#ifdef TGA_READ_USE_FREAD
         for (k=0;k<bytes2read+1;k++)
            p[k] = buffer[buffptr++];
#else
         if ((int)fread(p,1,bytes2read+1,fptr) != bytes2read+1)
            return(4);
#endif
         j = p[0] & 0x7f;
         TGA_MergeBytes(&(image[n]),&(p[1]),bytes2read);
         n++;
         if (p[0] & 0x80) {         /* RLE chunk */       
            for (i=0;i<j;i++) {
               TGA_MergeBytes(&(image[n]),&(p[1]),bytes2read);
               n++;
            }
         } else {                   /* Normal chunk */
            for (i=0;i<j;i++) {
               if ((int)fread(p,1,bytes2read,fptr) != bytes2read)
                  return(6);
               TGA_MergeBytes(&(image[n]),p,bytes2read);
               n++;
            }
         }
      }
   }

	/* Flip the image ? */
	if ((header.imagedescriptor & 0x20) == 32) 
		Flip_Bitmap(image,header.width,header.height,0);

	return(0);
}

void TGA_MergeBytes(BITMAP4 *pixel,unsigned char *p,int bytes)
{
   if (bytes == 4) {
      pixel->r = p[2];
      pixel->g = p[1];
      pixel->b = p[0];
      pixel->a = p[3];
   } else if (bytes == 3) {
      pixel->r = p[2];
      pixel->g = p[1];
      pixel->b = p[0];
      pixel->a = 255; 
   } else if (bytes == 2) {
      pixel->r = (p[1] & 0x7c) << 1;
      pixel->g = ((p[1] & 0x03) << 6) | ((p[0] & 0xe0) >> 2);
      pixel->b = (p[0] & 0x1f) << 3;
      pixel->a = (p[1] & 0x80);
   } else if (bytes == 1) {
      pixel->r = p[0];
      pixel->g = p[0];
      pixel->b = p[0];
      pixel->a = 255;
	}
}

BITMAP4 YUV_to_Bitmap(int y,int u,int v)
{  
   int r,g,b; 
   BITMAP4 bm = {0,0,0,0};
   
   // u and v are +-0.5
   u -= 128;
   v -= 128;
   
   r = y + 1.370705 * v;
   g = y - 0.698001 * v - 0.337633 * u;
   b = y + 1.732446 * u;

/*
   r = y + 1.402 * v;
   g = y - 0.344 * u - 0.714 * v;
   b = y + 1.772 * u;
*/
/*
   y -= 16;
   r = 1.164 * y + 1.596 * v;
   g = 1.164 * y - 0.392 * u - 0.813 * v;
   b = 1.164 * y + 2.017 * u;
*/

   if (r < 0) r = 0;
   if (g < 0) g = 0;
   if (b < 0) b = 0;
   if (r > 255) r = 255;
   if (g > 255) g = 255;
   if (b > 255) b = 255;
   bm.r = r;
   bm.g = g;
   bm.b = b;
   bm.a = 0;

   return(bm);
}


