#include <stdio.h>
#include "v_demo:graph.h"

#define PROGNAME "vdisplay"
#define MAXX 512
#define MAXY 512
#define MAXLARGE 8              /* maximum enlargement */
#define DW 16                   /* dither matrix width */
#define DP 4                    /* dither "pixel" width */

#define MAPF "map.dat"          /* default mapfile */

#define MAPPLANES 8             /* planes of display */
#define FSTOREX 128             /* bytes */
#define FSTOREW 64              /* words */
#define FSTOREL 32              /* longwords */
#define MAPLENGTH 256           /* number of addresses in colour map */
#define BRIGHT 255              /* top of colour map */
#define IOSCALE  3              /* MAPPLANES - (no of planes of grey) */

#define GREYMASK 0377           /* masks for enable */
#define REDMASK 017
#define GREENMASK 0360


/* convert v for monochrome colour map, so as to drive all 3 channels */
#define grey(v) (v+(v<<5)+(v<<10))
#define redgreen(r,g)  ((r<<1)+(g<<6))

short header;
short buf[MAXX];
short lastbuf[MAXX];
short col = WHITE;
short thresh = 0;
short xsize=MAXX;
short xs=MAXX;
short frameno = 0;
short ysize=0;
short large=1;                    /* picture enlargement factor */
char  * topleft;                  /* position of topleft of image, offset from frame */
FILE *fp = NULL;
char stereo=0;                    /* stereo pairs as anaglyphs */
char clearflag=1;                 /* clear unless -f or -o flag */
char sflag = 0;                   /* sole picture, displayed in the middle */
char dflag = 1;                   /* default is dither */
char overlay = 0;                 /* overlay one image on last */
char gflag =0;                    /* display thresholded gradients */
char zflag=0;                     /* dont use 32 greyval colourmap */
char *mapfile ;                   /* name of colour map file */

short dither [DW][DW];
short ditherp[DP][DP] = {   0, 16, 48, 96,
                           32, 64,112,160,
                           80,128,176,208,
                           142,192,224,240};

short lookup[2*MAPLENGTH];

main(argc,argv)
char **argv;
{
  short mask;  /* mask for enable */

  header=1;
  argv++;

  while((argc>0)&&(*argv[0] == '-'))
  {
    switch((*argv)[1]) {
    case 'x':
      sscanf(*argv+2,"%hd",&xsize);
      header=0;
      break;
    case 'f':  /* frame number */ 
      sscanf(*argv+2,"%hd",&frameno);
      clearflag=0;
      break;
    case 'o':            /* overlay used only with -z */
      sscanf(*argv+2,"%hd",&col);
      overlay=1;
      zflag=1;    
      clearflag=0;
      break;
    case 's':
      sflag=1;
      break;
    case 'g':
      gflag=1;
      sscanf(*argv+2,"%hd",&thresh);
      break;
    case 'd':      /* no dither - use with retouch */
      dflag=0;
      break;
    case 'c':
      sscanf(*argv+2,"%hd",&col);
      break;
    case 'm':      /* mapfile name */
      mapfile= *argv+2;
      if((fp = fopen(mapfile,"r")) == NULL)
        if((fp = fopen(MAPF,"r")) == NULL)
          error("can't open mapfile");
      break;
    case 'z':  /* for simulated grey without colour map */
      zflag=1;
      break;
    case 'l': 
      sscanf(*argv+2,"%hd",&large);
      if (large<1)
        large=1;
      break;
    case 'a':   /* anaglyphs */
      stereo=1;
      break;
    default:
      error("bad option");
      break;
    }
    argc--;
    argv++;
  }

  makedither();  /* set up dither matrix */

  setupdmap();   /* default colour map and lookup table */
  readmap();     /* lookup table from file, if any */
  if (zflag)
    resetmap();  /* reset to level 1 graphics config */

  if (clearflag)
    clearscreen();

  if (stereo)
  {
    mask=GREENMASK;
    anamap();      /* set up colour map for red/green */
  }
  else
  {
    mask=GREYMASK;
  }

  if (argc<2)
    error("picture input on stdin not implemented");

  while (--argc>0) 
  {
    if((fp=fopen(*(argv++),"r"))==NULL)
      error("can't open picture file");
    if (stereo)
      display(mask,MAPPLANES/2);
    else if (dflag)
      display(mask,IOSCALE);
    else
      display(mask,0);
    fclose(fp);
   
    if (stereo)
    {
      if (mask==GREENMASK)
      {
        mask = REDMASK;
      }
      else
      {
        mask=GREENMASK;
        frameno++;
      }
    }
    else
      frameno++;
  }
}


error(s)
char *s;
{
  fprintf(stderr,"%s: %s\n",PROGNAME,s);
  offset(0,0);
  exit(0);
}


makedither()
{
  register short i,j;
  short scale;

  for (i=0;i<DW;i++)
  {
    for (j=0;j<DW;j++)
    {
      dither[i][j] = ditherp[(i%DP)][(j%DP)] ;
      dither[i][j] +=  ((ditherp[(i/DP)][(j/DP)])/(DP*DP));
    }
  }

/* grey level display uses dither values >>(MAPPLANES-IOSCALE) */
/* anaglyph uses >> (MAPPLANES/2) */

  if ((!zflag)||stereo)
  {
    scale= stereo?MAPPLANES/2:MAPPLANES-IOSCALE;
    for (i=0; i<DW; i++)
      for (j=0; j<DW; j++)
        dither[i][j] = dither[i][j] >> scale;
  }
}


copybuf()
{
register short *lp, *p, i;

  for (p=buf,lp=lastbuf,i=0; i<xs; i++)
    *p++ = *lp++;
}


grad()
{
register short j,d;
register short *lp, *p,hv;

  p=buf;
  lp=lastbuf;

  if(ysize==0)
  {
    for(j=1;j<xs;j++)
    {
      d= *(p+1) - *p;
      *lp++ = *p;
      *p++ = ((d>thresh)||(d< -thresh))?BRIGHT:0;
    }
    *lp= *p;
  }
  else
  {
    for(j=1;j<xs;j++)
    {
      hv=0;
      d= *lp - *p;
      if ((d>thresh)||(d< -thresh))
        hv=BRIGHT;
      d= *(p+1) - *p;
      if ((d>thresh)||(d< -thresh))
        hv=BRIGHT;
      *lp++ = *p;
      *p++ = hv;
    }
      d= *lp- *p;
      *lp= *p;
      *p = ((d>thresh)||(d< -thresh))?BRIGHT:0;
  }
}



readline()
{
  register short *p, i;

  for (p=buf,i=0; i++ <xsize;)
    *p++ =(getc(fp))&0377;
}



enlarge(large)
{
  register short *pi, *pj, i;

  for (pi=buf+xsize,pj=buf+xs;pi>buf;)
  {
    --pi;
    for (i=0;i<large;i++)
      *--pj= *pi;
  }
}


border(x,y,size)
{
  enable(GREYMASK);
  if (zflag)
    colour(col);
  else
    colour(BRIGHT);

  x--;y++; /* alignment fudge */
  xline(x,      y,      size);
  xline(x,      y-size, size);
  yline(x,      y-size, size);
  yline(x+size, y-size, size);
}

xline(x,y,l)
int x,y,l;
{
register int i;

  for(i=l/2;i<l;i++) /* horizontal length fudge */
    plot(x++,y);  
}

yline(x,y,l)
int x,y,l;
{
register int i;

  for(i=0;i<l;i++)
    plot(x,y++);  
}

clearscreen()
{
register long  *p;
short i;

  colour(BLACK);
  enable(GREYMASK);
  
  p= (long *)frame;
  for (i=0; i<4096; i++)
  {
    *p++ = 0xFFFFFFFF;
    *p++ = 0xFFFFFFFF;
    *p++ = 0xFFFFFFFF;
    *p++ = 0xFFFFFFFF;
    *p++ = 0xFFFFFFFF;
    *p++ = 0xFFFFFFFF;
    *p++ = 0xFFFFFFFF;
    *p++ = 0xFFFFFFFF;
  }
}


    



setupdmap()  /* set up default & hardware colour maps */
{
short i,c,cmax,cscale;

  cmax=(MAPLENGTH>>IOSCALE)-1;
  cscale = 0;
  if (!dflag)
    cscale=IOSCALE;

  for (i=0; i<MAPLENGTH; i++)
  {
    c=i>>cscale;
    c=(c>cmax)?cmax:c;
    colourmap[2*i+1] = grey(c);
    lookup[i] = i;
  }

  c=i;
  for (i=0;i<MAPLENGTH;i++)   
  {
    lookup[i+MAPLENGTH] = c;   /* saturate */
  }
}




readmap()
{
  short i,v;
  if(fp == NULL)
  {
    return(0);
  }
  for (i=0;i<MAPLENGTH;i++)
  {
    v = 0377&getc(fp);
    lookup[i]=v;
  }
  fclose(fp);
}




anamap()  /* set up hardware colour map for anaglyph */
{
short i,r,g;

  for (i=0; i<MAPLENGTH; i++)
  {
    r=i&REDMASK;
    g=(i&GREENMASK)>>(MAPPLANES>>1);
    colourmap[2*i+1] = redgreen(r,g);
  }
}





disgrey_line(po,rp)
char *po;
short rp;   /* no of grey planes to be simulated by dither */
{
register unsigned short bit;
register short *pi,*pw,i;
register short *di, c, r, rmask;
short bp;
short *dirow;
char right;


/*display the line*/
  bp = MAPPLANES-rp;
  rmask = 1<<rp -1;

  if (zflag)
  {
    dirow = dither[(ysize&(DW-1))];
    for (pi=buf,pw= (short *)po,i=0; i<xs;pw++)
      for(di=dirow,bit=0100000;(bit!=0)&&(i++<xs);bit=(bit>>1))
      {
        if (lookup[(*pi++)] > *di++)
          colour(col);
        else
          colour(BLACK);
        *pw = bit;
      }
  }
  else if (stereo)    /* grey stereo */
  {
    dirow = dither[(ysize&(DW-1))];
    for (pi=buf,pw= (short *)po,i=0; i<xs;pw++)
    {
      for(di=dirow,bit=0100000;(bit!=0)&&(i++<xs);bit=(bit>>1))
      {
        c=lookup[(*pi++)];
        r = c & rmask;
        c = c>>rp;
        if (r > *di++)
          c++;
        c = (c>rmask)?rmask:c;
        c |= (c<<rp);
        colour(c);
        *pw = bit;
      }
    }
  }
  else
  {
    dirow = dither[(ysize&(DW-1))];
    for (pi=buf,pw= (short *)po,i=0; i<xs;pw++)
    {
      for(di=dirow,bit=0100000;(bit!=0)&&(i++<xs);bit=(bit>>1))
      {
        c=lookup[(*pi++)];
        r = c & rmask;
        c = c>>rp;
        if (r > *di++)
          c++;
        colour(c);
        *pw = bit;
      }
    }
  }

  return(0);
}



display(mask,rp)
short mask;   /* mask for enable - used to select red/green */
short rp;   /* no of grey planes to be simulated by dither */
{
char *po;
short xoff, yoff, i, maxframe;
long offs;

  if (header)
    xsize=getc(fp)&0377;


  xs=xsize*large;
  if(xs>MAXX)
    error("picture too large");
  if(large>MAXLARGE)
    error("enlargement factor too big");

  maxframe = YPIXELS/ (xs);
  if (sflag) {
    yoff = (YPIXELS>>1) + (xs>>1) + 4;
    xoff = (XPIXELS>>4) - (xs>>4) +1;
  }
  else {
    if (frameno>=maxframe*maxframe)
      frameno=0;
    yoff = frameno/maxframe;
    xoff= frameno - yoff*maxframe;
    yoff = (yoff+1) *(xs)+4;
    xoff = xoff * ((xs>>3) + 4);
  }

  offs = (( FSTOREX*yoff +xoff) & (~01));
  topleft = frame + offs;
    /* word aligned*/
  if (zflag)
  {
    if (overlay)
      enable(col);
    else
      enable(ALL);
  }
  else
    enable(mask);

  po = topleft;

  offset(0,0);

  for (ysize=0; ysize<xs;)
  {
    readline();
    if (large>1)
      enlarge(large);
    for (i=0;i<large;i++,ysize++)
    {
      if (gflag)
        grad();
      disgrey_line(po,rp);
      po -= FSTOREX;
      if (gflag)
        copybuf();  /* reinstate buf */
    }
  }

  border((offs%FSTOREX)*8+1,offs/FSTOREX-1,xs-1);
}
