#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include "int32.h"
#ifdef __TURBOC__
#include <mem.h>
#include <alloc.h>
#else
#define farcalloc calloc
#define farcoreleft() (1200000)
#endif
#ifdef unix
#ifndef EXIT_FAILURE
#define EXIT_FAILURE 1
#endif
#endif
#if (defined DJ || defined EMXOS2 )              /* a.r. */
#define EXIT_FAILURE 1
#endif

#include "bmp.h"
#define CLIPSIZE ((int32) PWIDTH* (int32) sizeof(int)* (int32) (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");

        { int32 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;
        uint32 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,int32 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;
        if (y>b->ny+1) goto return_null;
        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) {
return_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;
   int32 total=0;

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


