#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#ifdef __TURBOC__
#include <mem.h>
#include <alloc.h>
#else
#define farcalloc calloc
#define farcoreleft() (1200000)
#endif
#ifdef unix
#define EXIT_FAILURE 1
#endif

#include "bmp.h"
#define CLIPSIZE ((long) PWIDTH* (long) sizeof(int)* (long) (b->ny+2))
#define dbg if (gdebug==true)

#define false  0
#define true !0
extern int gdebug;
int iscolor;
extern int dev_noclip;

int row_show(char *s,Run *r,int y);
int row_init(Run *r, int ny);
int bmp_paintsort(int *p, int np);
int row_free(Run *r, int ny);
int row_grow(Run *r, int y, int v);
int bmp_ink4(Bitmap *bb,unsigned int savem,int cindex);
int bmp_ink16(Bitmap *bb,unsigned int newink[],int cindex);
int bmpcheck(Bitmap *b);

/*
	Filling routines to allow an arbitrary dimensioned
	bitmap to be drawn into.
*/

/* General notes:
	Color and pattern are the same thing, on a
	color system the pattern array would have the
	same depth as the bitmap itself.

	White bit = 1;
	Black bit = 0;
*/


/*	allocates the storage required.
	initializes it
	returns NULL if not enough memory
	returns pointer to Bitmap structure
	(ignoring depth at the moment)

*/

/* FILE *fgle; */
Bitmap *bmp_open(int xsize,int ysize,int depth, int bmp_compress)
{
	Bitmap *b;
	int *p;
	int i,j;

/*	fgle = fopen("test.gle","w");
	fprintf(fgle,"size 15 15\n");
	fprintf(fgle,"scale .3 .3 \n");
*/
	b = (Bitmap *) calloc(1,sizeof(Bitmap));
	if (b==NULL) bmp_die("No memory for Bitmap structure\n");
	b->nx = xsize;
	b->ny = ysize;
        b->compress = bmp_compress;

	row_init(&b->clip,b->ny);
	row_init(&b->paint,b->ny);
	row_init(&b->horiz,b->ny);

	b->data = (char *(*)[]) calloc(1,sizeof(int *) * (ysize+3));
	if (b->data == NULL) bmp_die("No memory for bitmap pointers \n");

	{ long x;
		if (iscolor) x=xsize/2;
		else x=xsize/8;
		x = x*ysize+80000L;
		if (farcoreleft() < x) b->compress = true;
	}
	if (b->compress) printf("Compressing internal bitmap to save memory\n");

	if (b->compress==false) {
	  for (i=0; i<=ysize+1; i++) {
	    if (iscolor) {
		if ( ((*b->data)[i] = calloc(1,xsize/2+2)) == NULL)
		    bmp_die("No memory for bitmap\n");
		memset( (*b->data)[i], 255, xsize/2+2);
	    } else {
		if ( ((*b->data)[i] = calloc(1,xsize/8+2)) == NULL)
		    bmp_die("No memory for bitmap\n");
		memset( (*b->data)[i], 255, xsize/8+2);
	    }
	  }
	}
	b->painty1 = ysize;
	b->lwidth = 1;
	b->lstyle = 0x8000001e;
	b->lstyle_mask = 0x80000000;
        b->lstyle_dotlen = 1;
        b->lstyle_dotpix = 0;
	bmp_color(b,0);
#ifdef __TURBOC__
/*	printf("Free memory after bitmap allocated %ld \n",farcoreleft()); */
#endif
	return b;
}
row_init(Run *r, int ny)
{
	r->alloc = (unsigned char *) calloc(1,ny+3);
	if (r->alloc == NULL) bmp_die("Not enough  memory for run counts \n");
	r->used = (unsigned char *) calloc(1,ny+3);
	if (r->used == NULL) bmp_die("Not enough  memory for run counts \n");
	r->row = (typrow) calloc(sizeof(int *),ny+3);
	if (r->row == NULL) bmp_die("Not enough  memory for run counts \n");
}
row_free(Run *r, int ny)
{
	int i,*rr;
	for (i=0;i<=ny;i++) {
		rr = (*r->row)[i];
		if (rr!=NULL)  free(rr);
	}
	free(r->row);
	free(r->used);
	free(r->alloc);
}
int row_shrinkx(Run *r, int ny);
int row_shrink(Bitmap *b);
row_shrink(Bitmap *b)
{
	row_shrinkx(&b->paint,b->ny);
	row_shrinkx(&b->horiz,b->ny);
}
row_shrinkx(Run *r, int ny)
{
	int i,*rr;
	for (i=0;i<=ny;i++) {
		rr = (*r->row)[i];
		if (rr!=NULL)  free(rr);
		(*r->row)[i] = NULL;
		r->used[i] = 0;
		r->alloc[i] = 0;
	}
}
#define row_data(r,y)   ( (*r.row)[y] )
#define row_used(r,y)   ( r.used[y] )
#define row_setused(r,y,v) ( r.used[y] = v )
row_grow(Run *r, int y, int v)
{
	int vv;
	int *z;
	v += r->used[y];
	if (v<= r->alloc[y]) {
		r->used[y] = v;
		return;
	}
	vv = v;
	if ((v&1)==1) vv++;
	z = (*r->row)[y] ;
	if (z==NULL) z = (int *) malloc(vv*sizeof(int));
	else z = (int *) realloc((*r->row)[y],vv*sizeof(int));
/*
	if (z==NULL) z = malloc(vv*sizeof(int));
	else {
		z = malloc(vv*sizeof(int));
		memcpy(z, (*r->row)[y] ,r->used[y]*sizeof(int));
		free( (*r->row)[y] );
	}
*/
	(*r->row)[y] = z;
		/* malloc(v);   */
	if ((*r->row)[y] == NULL) bmp_die("Could not grow row count data \n");
	r->alloc[y] = vv;
	r->used[y] = v;
}

/*	frees up all storage. */
iffree(void *block)
{
	if (block!=NULL) free(block);
}
bmp_close(Bitmap *b)
{
	int i,j;
	if (b==NULL) return;
	for (i=0; i<=b->ny+2; i++) {
		iffree((*b->data)[i]);
	}
	row_free(&b->paint,b->ny);
	row_free(&b->clip,b->ny);
	row_free(&b->horiz,b->ny);
/*	fclose(fgle);  */
}

/*
	Draws line of current lwidth, lstyle,
	and color/pattern

	Line is clipped using clip array.
*/
#define sign(x) ((x) > 0 ? 1:  ((x) == 0 ? 0:  (-1)))
bmp_line(Bitmap *b,int x1,int y1,int x2,int y2)
{
	int dx, dy, dxabs, dyabs, i, j, px, py, sdx, sdy;
	register int x,y;
	unsigned long int mask,pattern,lastpatbit;
	int lwidth, dotlen,pix;

	lwidth = b->lwidth/2;
	mask = b->lstyle_mask;
	pattern = b->lstyle;
	lastpatbit = 1l << (pattern & 0x1f);
	dotlen = b->lstyle_dotlen;
	pix = b->lstyle_dotpix;

/* 	printf("lstyle %08lx, mask %08lx, dotlen %d, dotpix %d, lastpatbit %8lx pat= %lx \n",
	pattern,mask,dotlen,pix,lastpatbit,pattern & 0x1f);
*/
	if (bmp_clipline(b,&x1,&y1,&x2,&y2)) return;    /* clip to bitmap */
	dx = x2 - x1;
	dy = y2 - y1;
	sdx = sign(dx);
	sdy = sign(dy);
	dxabs = abs(dx);
	dyabs = abs(dy);
	x = 0;
	y = 0;
	px = x1;
	py = y1;

	if (dxabs >= dyabs) {
	   mask = (mask & lastpatbit) ? 0x80000000 : mask;
	   if (pattern & mask) {
	      for (j= -lwidth; j<=lwidth; j++)
		bmp_pixel(b,px,py+j);
	   }
	   if ((++pix) >= dotlen)  {mask >>= 1; pix=0;}
	   for (i=0; i<dxabs; i++) {
	      mask = (mask & lastpatbit) ? 0x80000000 : mask;
	      y += dyabs;
	      if (y>=dxabs) {
		 y -= dxabs;
		 py += sdy;
	      }
	      px += sdx;
	      if (pattern & mask) {
		 for (j= -lwidth; j<=lwidth; j++)
		    bmp_pixel(b,px,py+j);
	      }
	      if ((++pix) >= dotlen)  {mask >>= 1; pix=0;}
	   }
	} else {
	   mask = (mask & lastpatbit) ? 0x80000000 : mask;
	   if (pattern & mask) {
	      for (j= -lwidth; j<=lwidth; j++)
		 bmp_pixel(b,px+j,py);
	   }
	   if ((++pix) >= dotlen)  {mask >>= 1; pix=0;}
	   for (i=0; i<dyabs; i++) {
	      mask = (mask & lastpatbit) ? 0x80000000 : mask;
	      x += dxabs;
	      if (x>=dyabs) {
		 x -= dyabs;
		 px += sdx;
	      }
	      py += sdy;
	      if (pattern & mask) {
		 for (j= -lwidth; j<=lwidth; j++)
		    bmp_pixel(b,px+j,py);
	      }
	      if ((++pix) >= dotlen)  {mask >>= 1; pix=0;}
	   }
	}

	b->lstyle_mask = mask;
	b->lstyle_dotpix = pix;
/*	printf("lstyle %08x, mask %08x, lastpatbix %08x, dotpix %d \n",
	       pattern, mask, lastpatbit, pix); */

}

/*
	Apply clipping
	Get bit from ink
	Get row of bitmap
	Place bit into row
*/
static Bitmap *pr_lb;
static int pr_ly;
static char *pr_row;
bmp_pixel(register Bitmap *b,register int x,register int y)
{
	static int i,spot;
	int ry;
/*	static char *row; */
	unsigned char *k;

	if (b->clipping) if (bmp_invis(b,x,y)) return;
	if (x> b->nx || x<0 || y>b->ny || y<0) return;
	ry = y % 16;
	if (pr_ly!=y || pr_lb!=b) {
		pr_row = bmp_row(b,y);
		pr_ly = y; pr_lb = b;
	}

	k = (unsigned char *) &b->ink[ry%16][0];
	spot = k[x%16];
	if (iscolor) {
		i = pr_row[x/2];
		if (x % 2==0) {
			i = (i & 0x0f) | (spot << 4);
		} else {
			i = (i & 0xf0) | spot;
		}
		pr_row[x/2] = i;
		return;
	}


	if (k[x % 16] != 0) { /* a white spot */
	  *(pr_row + x/8) |= (1 << (x%8));
	} else {
	  *(pr_row + x/8) &= (255-(1 << (x%8)));
	}
}
bmp_paintrange(Bitmap *b,int y, register int x1, int x2)
{
	static int i,spot;
	int ry;
/*	static char *p;*/
	unsigned char *k;

/* debugging stuff */
	if (x1==-2 || x2==-2) {
		printf("********Stuffed range on line %d, %d-->%d \n",y,x1,x2);
		return;
	}
	dbg printf("Paint range %d, %d %d \n",y,x1,x2);

/* end debug */
/*	printf("Paint range %d, %d %d \n",y,x1,x2);*/
	ry = y % 16;
	if (pr_ly!=y || pr_lb!=b) {
		pr_row = bmp_row(b,y);
		pr_ly = y; pr_lb = b;
	}


	k = (unsigned char  *) &b->ink[ry%16][0];

	if (iscolor) {
	  for (;x1<=x2;x1++) {
		spot = k[x1%16];
		i = pr_row[x1/2];
		if (x1 % 2==0) {
			i = (i & 0x0f) | (spot << 4);
		} else {
			i = (i & 0xf0) | spot;
		}
		pr_row[x1/2] = i;
	  }
	  return;
	}

	for (;x1<=x2;x1++) {
	  if (k[x1 % 16] != 0) { /* a white spot */
		*(pr_row + x1/8) |= (1 << (x1%8));
	  } else {
		*(pr_row + x1/8) &= (255-(1 << (x1%8)));
	  }
	}
}

/* return true if pixel at that
   location is clipped
*/
int bmp_invis(Bitmap *b, int x, int y)
{
	int *c,nc,i;

	if (! b->clipping) return false; /* clipping os off */
	c = row_data(b->clip,y);
	if (*c==0) return true; /* clipping */
	nc = row_used(b->clip,y);
	for (i=0;i<nc-1;i+=2, c+=2) {
	     if ( (x>= *c) && (x<= *(c+1))) return false; /* is visible */
	}
	return true;
}

/*
	Adds x values at y crossings onto the paint[] array.
	Doesn't add last crossing
	Sets maxy,miny

	Special cases:

move
	init lddy
		sx,sy, x1,y1 ...

	* Change in sdy
		put in double x value
	* When end of line meets start of line.
		remember start of line with bmp_pmove
		remember start sdy
		if sdy == enddy then don't put endx,endy
		start new polygon.
		(at fill check lines is closed, if not then close before fill)
	* (WRONG) Start of Horizontal lines
		put in start xvalue, (if not first line in path)
	* Continued horizontal line
		do nothing
	* (WRONG) end of horizontal line
		Put in last x(thisy) value

	* with horizontal lines or lines which have multiple x values
	store the range of values into b->horiz[][] so they can
	also be drawn in.  This gets the middle of an H or A or top
	of a B drawn correctly if the bar is very thin.
*/
bmp_pmove(Bitmap *b,int x1,int y1)
{
	bmp_clipline(b,&x1,&y1,&x1,&y1);    /* clip to bitmap */
	/* fprintf(fgle,"amove %d %d \n",x1,y1); */
	if (b->sx != b->x1 || b->sy != b->y1) {
		printf("Closing path\n");
		bmp_pline(b,b->sx,b->sy);
	}
	/* printf("pmove %d %d \n",x1,y1); */
	b->sx = x1;	b->sy = y1;
	b->x1 = x1;	b->y1 = y1;
	b->sddy = -2;
	b->lddy = -2;
}
bmp_pline(Bitmap *b,int x2,int y2)
{
	int dx, dy, dxabs, dyabs, i, j, px, py, sdx, sdy;
	int endy,xwid;
	register int x,y;
	int x1=b->x1, y1=b->y1;	/* start of line */
	int lddy=b->lddy;
	int ddy,horizx;

	if (x1==x2 && y1==y2) return;	/* not going anywhere */



	if (bmp_clipline(b,&x1,&y1,&x2,&y2)) return;    /* clip to bitmap */
/*	fprintf(fgle,"aline %d %d \n",x2,y2);
	printf("CVector %d,%d  --- %d,%d \n",x1,y1,x2,y2);
*/
	b->x1 = x2; b->y1 = y2;		/* remember end of line */
	bmp_minmax(b,y1);
	bmp_minmax(b,y2); 	/* remember range of y paint values */

	dx = x2 - x1;
	dy = y2 - y1;
	xwid = dx/2;
	sdx = sign(dx);
	sdy = sign(dy);
	dxabs = abs(dx);
	dyabs = abs(dy);
	x = 0;
	y = 0;
	px = x1;
	py = y1;
	endy = y2;

	ddy = sdy;
	if (dy==0) ddy = 0;	/* ddy = -1, 0, 1 */
	if (ddy==0) {	/* this is a horizontal line */
		bmp_painth(b,x1,x2,y1); 	/* paint the whole line */
		goto endpline;
	}
	if (lddy == -ddy)  bmp_paintx(b,x1,y1); /* put in last endxy */
	if (dxabs >= dyabs) {
	   horizx = px+sdx; /* start of first row */
	   for (i=0; i<dxabs; i++) {
	      y += dyabs;
	      if (y>=dxabs) {
		 y -= dxabs;
		 bmp_painth(b,horizx,px,py); 	/* end of that row */
		 py += sdy;
		 horizx = px+sdx; 	/* start of next row */
		 /* if (py==endy) break; */
		 bmp_paintx(b,px+sdx,py);
	      }
	      px += sdx;
	   }
	} else {
	   for (i=0; i<dyabs; i++) {
	      x += dxabs;
	      if (x>=dyabs) {
		 x -= dyabs;
		 px += sdx;
	      }
	      py += sdy;
	      bmp_paintx(b,px,py);
	   }
	}

endpline:
	if (ddy!=0) {	/* remember last line if not horiz */
		b->lddy = ddy;
 		if (lddy== -2) { /* first line */
			b->sddy = ddy;
			b->ssx = x1;
			b->ssy = y1;
			return;
		}
	}

	if (x2==b->sx && y2==b->sy) { /* path is closed */
		if (b->sddy == -b->lddy) { /* direction changed */
			bmp_paintx(b,b->ssx,b->ssy);
		}
	}
}

bmp_minmax(Bitmap *b,int y) 	/* remember range of y paint values */
{
	if (y<b->painty1) b->painty1 = y;
	if (y>b->painty2) b->painty2 = y;
}

/*
	Adds x value onto paint[] range table
*/
bmpcheck(Bitmap *b)
{
	int i,*p;

	for (i=0;i<16;i++) {
/*		p = &(*b->paint)[i][0];
		printf("P[%d] = %d %d %d %d \n",i,*p,*(p+1),*(p+2),*(p+3));
*/
	}
}
bmp_paintx(Bitmap *b, int x, int y)
{
	int *c,nc,i,nrow;


	i = 0;
	row_grow(&b->paint,y,1);
	c = row_data(b->paint,y);
	c += row_used(b->paint,y) - 1;
	*c = x;

	c = row_data(b->paint,y);
	nc = row_used(b->paint,y);
/*
	printf("Paint [%d] x=%d , ",y,x);
	for (i=0;i<nc;i++,c++) {
		printf("%d ",*c);
	}
	printf("\n");
*/
}
row_show(char *s,Run *r,int y)
{
	int i,*c,nc;

	c = (*r->row)[y];
	nc = r->used[y];
	printf("show {%s} [%d] ",s,y);
	for (i=0;i<nc;i++,c++) {
		printf("%d ",*c);
	}
	printf("\n");
}
/*
	This routine remembers the horizontal lines that
	need to be drawn.

	They don't need sorting, and they can be amalgamated e.g.
	They do need putting into ascending order when used.

		1 7, 5 9,  can become 1 9
*/
showhy(Bitmap *b, int y)
{
	int *c,i;

	i = 0;
/*	c = &(*b->horiz)[y][0];
	c = (*b->horiz.row)[y];
	printf("showy ");
	for (;i < (*b->horiz.used)[y];c+=2 ,i+=2) {
		printf("%d %d ",*c,*(c+1));
	}
	printf("\n");
*/
}
bmp_painth(Bitmap *b, int x1,int x2, int y)
{
	int *c,i,nc;

	/* printf("Addh, (%d) %d %d \n",y,x1,x2); */

	if (x1>x2) {i=x2; x2=x1; x1=i;}
	i = 0;
	c = row_data(b->horiz,y);
	nc = row_used(b->horiz,y);
	for (;i < nc-1 ;c+=2 ,i+=2) {
		if (*c<=x1 && *(c+1)>=x1) {
			c++;
			if (*c < x2) *c = x2;
			return;
		}
		if (*c<=x2 && *(c+1)>=x2) {
			if (*c > x1) *c = x1;
			return;
		}
		if (*c-1 == x2) { *c = x1; return;}
		if (*(c+1)+1 == x1) { *(c+1) = x2; return;}
	}
	/* found an empty slot */

	row_grow(&b->horiz,y,2); /* make it two bigger */
	c = row_data(b->horiz,y);
	nc = row_used(b->horiz,y);
	c += nc - 2;
	*c = x1;
	*(c+1) = x2;
}

/* Clear the current path. */
bmp_newpath(Bitmap *b)
{
	int i,j;
	int *c;

	for (i=b->painty1; i<=b->painty2; i++) {
		row_setused(b->paint,i,0);
		row_setused(b->horiz,i,0);
	}
	b->painty1 = b->ny+2;
	b->painty2 = 0;

	if (farcoreleft()<50000L) row_shrink(b);
}

/*
  from painty1 to painty2
	sort x's and call bmp_paint(b,y,x1,x2);
*/
bmp_paintsort(int *p, int np)
{
	int *pp = p;
	int a,b,fixed,i;

	fixed = true;
	for (;fixed;) {
	   fixed = false;
	   for (p = pp, i = 0; i<(np-1); i++, p++) {
	      if (*p > *(p+1)) {
		a = *p;
		*p = *(p+1);
		*(p+1) = a;
		fixed=true;
	      }
	   }
	}
	p = pp;
/*
	printf("(sort) ");
	for (;*p!=-2;p++) printf("%d ",*p);
	printf("\n");
*/}


bmp_fill(Bitmap *b)
{
	int i,k,nrow,ncrow,j,x1,x2;
	int *c,*p,np,nc;

	/* printf ("Fill, color is %d \n",b->ink[0][0]); */
	if (b->sx != b->x1 || b->sy != b->y1) {
		printf("Closing path\n");
		bmp_pline(b,b->sx,b->sy);
	}
	for (i=b->painty1; i<=b->painty2; i++) {
	   p = row_data(b->paint,i);
	   np = row_used(b->paint,i);
	   bmp_paintsort(p,np);
	   for (j=0;j<np-1; j+=2, p+=2) {
	      c = row_data(b->clip,i);
	      nc = row_used(b->clip,i);
	      if (!b->clipping) bmp_paintrange(b,i,*p,*(p+1)); /* no clipping */
	      else {
		 for (k=0;k<nc-1; k+=2, c+=2) {
		   x1 = *p; x2 = *(p+1);
		   if (x1< *c) x1 = *c;
		   if (x2> *(c+1)) x2 = *(c+1);
		   if (x1<=x2) bmp_paintrange(b,i,x1,x2); /* clipped range */
		 }
	      }
	   }

	   /* now paint the thin horizontal bits */
	   p = row_data(b->horiz,i);
	   np = row_used(b->horiz,i);
	   for (j=0;j<np-1; j+=2, p+=2) {
	      if (!b->clipping) bmp_paintrange(b,i,*p,*(p+1)); /* no clipping */
	      else {
	         c = row_data(b->clip,i);
	         nc = row_used(b->clip,i);
	         for (k=0;k<nc-1; k+=2, c+=2) {
		   x1 = *p; x2 = *(p+1);
		   if (x1< *c) x1 = *c;
		   if (x2> *(c+1)) x2 = *(c+1);
		   if (x1<=x2) bmp_paintrange(b,i,x1,x2); /* clipped range */
		 }
	      }
	   }
	   /* end horizontal bits */
	}
}



/* Coppies bitmap on while applying clip (not using ink[])
*/
/*
bmp_copy(Bitmap *b,char *bitmap,int nx,int ny)
{
}
*/
/* Move paint array onto clip array.  */
bmp_clip(Bitmap *b)
{
	int i;
	int *c,*p,nc;

	if (dev_noclip) return;

	/* Sort the paint array before copying it */
	for (i=0;i<b->ny; i++) {
		c = row_data(b->paint,i);
		bmp_paintsort(c,row_used(b->paint,i));
	}

	/* Now simply copy it over */
	for (i=0;i<b->ny; i++) {
		p = row_data(b->paint,i);
		nc = row_used(b->paint,i);

		if (nc>0) {
			row_grow(&b->clip,i,nc);
			c = row_data(b->clip,i);
			memcpy(c,p,nc*sizeof(int));
		}

	}

	b->clipping = true; /* remeber that clipping is on now */
}

/* Clears the clipping array	*/
bmp_newclip(Bitmap *b)
{
	int i,j;
	int *c;

	if (dev_noclip) return;
	b->clipping = false;
	for (i=0; i<=b->ny+1; i++) {
		row_setused(b->clip,i,0);
	}
}

/*
	Coppies clipping array and lastclip pointer into malloced area
	and sets lastclip to point to this new malloced area.
*/
bmp_saveclip(Bitmap *b)
{
	int *c;

	if (dev_noclip) return;
	/* should save clipping region */
}

bmp_restoreclip(Bitmap *b)
{
	int *c;
	if (dev_noclip) return;
	/* should restore from saved memory but I'm in a hurry */
	b->clipping = false;

#ifdef __TURBOC__
	row_shrinkx(&b->clip,b->ny);
#endif
}

/*	Sets ink based on a lookup table
	255 = white, 0 = black
*/
bmp_color(Bitmap *b,int colindex)
{
	int i,j;
	for (i=0;i<16;i++)
	    for (j=0;j<16;j++)
		b->ink[i][j] = colindex;
}


unsigned int grey_bits[] = {0x0000, 0x0200, 0x0802, 0x0a02,
		0x5050, 0x5250, 0x5852, 0x5a52,
		0xa5a5, 0xa7a5, 0xada7, 0xafa7,
		0xf5f5, 0xf7f5, 0xfdf7, 0xfff7, 0xffff};


static unsigned int pat_shade1[] = {
	0xc000, 0x6000, 0x3000, 0x1800, 0xc00, 0x600, 0x300,
	0x180, 0xc0, 0x60, 0x30, 0x18, 0xc, 0x6, 0x3, 0x8001 };
static unsigned int pat_shade2[] = {
	0xc018, 0x600c, 0x3006, 0x1803, 0x8c01, 0xc600, 0x6300, 0x3180,
	0x18c0, 0xc60, 0x630, 0x318, 0x18c, 0xc6, 0x63, 0x8031 };
static unsigned int pat_shade3[] = {
	0xf078, 0x783c, 0x3c1e, 0x1e0f, 0x8f07, 0xc783, 0xe3c1, 0xf1e0,
	0x78f0, 0x3c78, 0x1e3c, 0xf1e, 0x78f, 0x83c7, 0xc1e3, 0xe0f1 };
static unsigned int pat_shade4[] = {
	0x7e00, 0x3f00, 0x1f80, 0xfc0, 0x7e0, 0x3f0, 0x1f8, 0xfc,
	0x7e, 0x3f, 0x801f, 0xc00f, 0xe007, 0xf003, 0xf801, 0xfc00 };
static unsigned int pat_shade5[] = {
    0x6006,   0x7ffe,0x6006,0x300c,0x1818,0xc30, 0x660, 0x3c0,
    0x660, 0xc30, 0x1998,0x33cc,0x67e6,0x7ffe,0x6006,   0x6006   };
static unsigned int pat_grid1[] = {
	0xc003, 0x6006, 0x300c, 0x1818, 0xc30, 0x660, 0x3c0, 0x180,
	0x3c0, 0x660, 0xc30, 0x1818, 0x300c, 0x6006, 0xc003, 0x8001 };
static unsigned int pat_grid2[] = {
	0xf00f, 0x781e, 0x3c3c, 0x1e78, 0xff0, 0xfe0, 0xfc0, 0xf80,
	0xfc0, 0xfe0, 0xff0, 0x1e78, 0x3c3c, 0x781e, 0xf00f, 0xf00f };
static unsigned int pat_grid3[] = {
	0xffff, 0xffff, 0x3c3c, 0x3c3c, 0x3c3c, 0x3c3c, 0x3c3c, 0xffff,
	0xffff, 0x3c3c, 0x3c3c, 0x3c3c, 0x3c3c, 0x3c3c, 0xffff, 0xffff};
static unsigned int pat_grid4[] = {
	0x303, 0x303, 0xcfc0, 0xcfc0, 0x3ff0, 0x3ff0, 0xfcc, 0xfcc,
	0x303, 0x303, 0xcc0f, 0xcc0f, 0xf03f, 0xf03f, 0xc0cf, 0xc0cf };
static unsigned int pat_grid5[] = {
	0x300, 0x300, 0x300, 0x300, 0xcc0, 0xcc0, 0xf03f, 0xf03f,
	0x3, 0x3, 0x3, 0x3, 0xc00c, 0xc00c, 0x3ff0, 0x3ff0 };


#define NGREY 17
bmp_setcolor(Bitmap *bb,int r, int g, int b, int ftyp)
{
	/* values between 0-255 */
	unsigned int i,j,m,nib,k,n,xx,savenib,ix,iy,savem;
	unsigned int *patt;
	int cindex;
	float f;

	f = (r*3+g*2+b)/(255*6.0);

	i = (1-f)*(NGREY-.8);
	if (f<1 && i==0) i = 1;

	savem = grey_bits[i];

	/* Fill types,
		2=pat_black, 3=pat_red, 4=pat_green,
		5=pat_blue, 6=pat_yellow
	*/

	if (ftyp>1 && ftyp<7) {
		cindex = 0;
		if (ftyp==3) cindex = 1;
		if (ftyp==4) cindex = 2;
		if (ftyp==5) cindex = 4;
		if (ftyp==6) cindex = 3;
		if (!iscolor) cindex = 0;
		if (g==0x0 && b==0x20) patt = pat_shade1;
		if (g==0x0 && b==0x0c) patt = pat_shade1;
		if (g==0x0 && b==0x10) patt = pat_shade2;
		if (r==0x5 && b==0x20) patt = pat_shade3;
		if (g==0x0 && b==0x40) patt = pat_shade4;
		if (g==0x0 && b==0x60) patt = pat_shade5;
		if (g==0x20 && b==0x20) patt = pat_grid1;
		if (g==0xf && b==0xf)   patt = pat_grid1;
		if (g==0x10 && b==0x10) patt = pat_grid2;
		if (g==0x20 && b==0x20) patt = pat_grid3;
		if (g==0x40 && b==0x40) patt = pat_grid4;
		if (g==0x60 && b==0x60) patt = pat_grid5;
		bmp_ink16(bb,patt,cindex);
		return;
	}
	/* 0 = black, 1=red, green, blue, yellow, magenta, brown, ... white */

	if (r==g && g==b) {
		bmp_ink4(bb,savem,0);
		return;
	}
	if (iscolor) {
		cindex = -1;
		if (r>20 && g==0 && b==0) {cindex = 1; f=r;}
		if (g>20 && r==0 && b==0) {cindex = 2; f=g;}
		if (b>20 && g==0 && r==0) {cindex = 4; f=b;}
		if (r>20 && g>20 && b==0) {cindex = 3; f=r;}
		if (cindex>=0) {
			f = f/255.0;
			i = (f)*(NGREY-.8);
			if (f<1 && i==(NGREY-1)) i = NGREY-2;
			bmp_ink4(bb,grey_bits[i],cindex);
			return;
		}
		if (r>150 && g<50 && b<50)  cindex = 1;
		if (r<50 && g>150 && b<50)  cindex = 2;
		if (r<50 && g<50 && b>150)  cindex = 4;
		if (r>150 && g>150 && b<50)  cindex = 3;
		if (b>50 && g>50 && r<18)    cindex = 6; /* CYAN */
		if (r>50 && b>50 && g<20)  cindex = 5; /* Magenta */
		if (cindex >= 0) {
			bmp_color(bb,cindex);
			return;
		}
	}
	bmp_ink4(bb,savem,0);
}
bmp_ink4(Bitmap *bb,unsigned int savem,int cindex)
{
	/* values between 0-255 */
	unsigned int i,j,m,nib,k,n,xx,savenib,ix,iy;
	unsigned int *patt;
	float f;

	for (ix=0; ix<16; ix+=4) {
	  for (iy=0; iy<16; iy+=4) {
		m = savem;
		for (j=0; j<4; j++) {
		  nib = m & 0xf;
		  for (k=0; k<4; k++) {
			if (nib&1) bb->ink[ix+j][iy+k] = cindex;
			else bb->ink[ix+j][iy+k]  = 15;
			nib = nib >> 1;
		  }
		  m = m >> 4;
		}
	  }
	}

}
bmp_ink16(Bitmap *bb,unsigned int newink[],int cindex)
{
	unsigned int m;
	int i,ix,iy;

	for (iy=0; iy<16; iy++) {
		m = newink[iy];
		for (ix=15; ix>=0; ix--) {
			if (m&1 == 1)  bb->ink[iy][ix] = cindex;
			else bb->ink[iy][ix]  = 15;
/*			printf("ix iy %d %d  %d\n",ix,iy,bb->ink[ix][iy]);
*/			m = m >> 1;
		}
	}
}
bmp_lwidth(Bitmap *b,int x)
{
	b->lwidth = x;
}

bmp_lstyle(Bitmap *b,long n,int l)
   /* n is a pattern, 01110001110;  l is len of one pattern dot in pixels */
{
	b->lstyle = n;
	b->lstyle_mask = 0x80000000;
	b->lstyle_dotlen = l;
	b->lstyle_dotpix = 0;
}

/* (depth must be 1, expands this into the pat[][] array,
	each 1 becomes a 255)
*/
/*
bmp_pattern(Bitmap *b,char *pat,int nxbytes,int nybytes,int depth)
{
}
*/
/* Returns pointer to a row of the bitmap */

unsigned char *bmp_expanded(Bitmap *b,int y);
char *bmp_row(Bitmap *b,int y)
{
	pr_ly = -1;
	if (b->compress) {
		return (char *) bmp_expanded(b,y);
	} else {
		return (*b->data)[y];
	}
}
/* this compression routine won't work with more than one 'Bitmap' as
it doesn't store it's data in Bitmap *b, like it should. */

unsigned char *bmp_expanded(Bitmap *b,int y)
{
	static unsigned char *bitrow,*bitc;
	static unsigned char *o,*bc,c,*lastv;
	static int lasty = -2;
	char *v;
	int nc,x,i,j,nxbyte;
	int totwid = 0;


	if (lasty==y) return bitrow;
	nxbyte = b->nx/8+1;
	if (iscolor) nxbyte = b->nx/2+2;
	if (bitrow==NULL) bitrow = (ucharp) calloc(1,nxbyte+3);
	if (bitc == NULL) bitc = (ucharp) calloc(1,nxbyte*2+3);
	if (bitrow==NULL || bitc==NULL) bmp_die("bitc allocation error\n");
	v = (*b->data)[y];

	if (lasty != -2) {
	  o = bitc;
/*	  if (lasty==296) {
		 printf("Store [%d]",lasty);
		  for (i=0;i<nxbyte;i++) printf("%x ",bitrow[i]);
		  printf("\n");
	  }
*/
	  for (x=0; x<nxbyte ; x++) {
		c = bitrow[x];
		nc = 1;
		for (;x<(nxbyte-1) && bitrow[x+1]==c && nc<250; x++) nc++;
		*o++ = nc;
		*o++ = c;
		totwid = totwid + 2;
	  }
	  if (totwid>nxbyte*2) printf("Total width %d nx=%d\n",totwid,nxbyte);
	 *o++ = 0;
	 *o++ = 0; /* nulls at end */
	 nc = o-bitc+1;


	 if (lastv!=NULL) lastv = (unsigned char *) realloc(lastv,nc);
	  else lastv = (ucharp) malloc(nc);
	  if (lastv==NULL) {
		printf("Failed to allocate enough memory for bitmap \n");
		abort();
	  }
	  memcpy(lastv,bitc,nc);
	  (*b->data)[lasty] = (char *) lastv;
	}
	lasty = y;
	lastv = (ucharp) v;

	nc = 0;
	if (v==NULL) {
		memset(bitrow,255,nxbyte);
		return bitrow;
	}

	nc = 0;
	bc = (ucharp) v;

	for (i=0, o = bitrow; bc[i]!=0 ; i+=2) {
		for (j=0; j<bc[i]; j++) {
			*o++ = bc[i+1]; nc++;
			if (nc>nxbyte) {

			printf("ERR[%d] ",y);
			for (i=0;bc[i]!=0;i+=2) printf("%d,%x ",bc[i],bc[i+1]);
			printf("\n");


				printf("gone past end [%d] %d %d \n",y,nxbyte,nc);
				return bitrow;
			}
		}
	}
/*
	if (y==296) {
		printf("EXP[%d] ",y);   for (i=0;i<10;i++) printf("%d ",bitrow[i]);
		printf("\n");
	}
*/
	return bitrow;
}
bmp_die(char *s)
{
	printf("%s",s);
	exit(EXIT_FAILURE);
}

bmp_pbezier(Bitmap *bb,int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
{
	float ax,bx,cx,ay,by,cy,xxx,yyy,t,nstep;
	int dist,i;



	dbg printf("pbezier, %d %d   %d %d   %d %d   %d %d \n",x0,y0,x1,y1,x2,y2,x3,y3);
	dist = abs(x3-x0) + abs(y3-y0);
	nstep = 20;
	if (dist<30) nstep = 10;
	if (dist<10) nstep = 5;
	if (dist<5) nstep = 3;
	if (dist<=2) {
		bmp_pline(bb,x3,y3);
		return;
	}
	cx = (x1-x0)*3;
	bx = (x2-x1)*3-cx;
	ax = x3-x0-cx-bx;
	cy = (y1-y0)*3;
	by = (y2-y1)*3-cy;
	ay = y3-y0-cy-by;
	for (i=0;i<=nstep;i++) {
		t = (float) i/nstep;
		xxx = ax*pow(t,3.0) + bx*t*t + cx*t + x0;
		yyy = ay*pow(t,3.0) + by*t*t + cy*t + y0;
		bmp_pline(bb,(int) xxx,(int) yyy);
	}
}
bmp_bezier(Bitmap *bb,int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
{
	float ax,bx,cx,ay,by,cy,xxx,yyy,t,nstep,xx,yy;
	int dist,i;

	dist = abs(x3-x0) + abs(y3-y0);
	nstep = 20;
	if (dist<30) nstep = 10;
	if (dist<10) nstep = 5;
	if (dist<5) nstep = 3;
	if (dist<=2) {
		bmp_line(bb,x0,y0,x3,y3);
		return;
	}
	cx = (x1-x0)*3;
	bx = (x2-x1)*3-cx;
	ax = x3-x0-cx-bx;
	cy = (y1-y0)*3;
	by = (y2-y1)*3-cy;
	ay = y3-y0-cy-by;
	xx = x0; yy = y0;
	for (i=0;i<=nstep;i++) {
		t = (float) i/nstep;
		xxx = ax*pow(t,3.0) + bx*t*t + cx*t + x0;
		yyy = ay*pow(t,3.0) + by*t*t + cy*t + y0;
		bmp_line(bb,(int) xx,(int) yy, (int) xxx, (int) yyy);
		xx = xxx; yy = yyy;
	}
}

#define OUTCODES_CSD( X, Y, OUTCODE )           \
    ( OUTCODE ) = 0;                            \
    if ((Y) >= b->ny )          OUTCODE |= 1; \
    else  if ((Y) < 0 )    OUTCODE |= 2; \
    if ((X) >= b->nx  )          OUTCODE |= 4; \
    else  if ((X) < 0 )    OUTCODE |= 8;

int bmp_clipline(Bitmap *b,int *x1, int *y1, int *x2, int *y2)
{
	int outcode1,outcode2;

	OUTCODES_CSD( *x2, *y2, outcode2 );
	OUTCODES_CSD( *x1, *y1, outcode1 );

    	if (outcode1 & outcode2)         /* trivial reject */
 		return true; /* throw line away */
	if (!(outcode2 | outcode1))    /* trivial accept */
 		return false;

	doclip(0,b->nx-1,x1);
	doclip(0,b->nx-1,x2);
	doclip(0,b->ny-1,y1);
	doclip(0,b->ny-1,y2);
	return false;
}
doclip(int min, int max, int *v)
{
	if (*v < min) *v = min;
	if (*v > max) *v = max;
}

showfree()
{
#ifdef __TURBOC__
   struct farheapinfo hi;
   long total=0;

   hi.ptr = NULL;
   while( farheapwalk( &hi ) == _HEAPOK ) {
	if (!hi.in_use) total += hi.size;
   }
   printf("Total mem free %ld \n",total);
#endif
}

