/* pbmtogo.c - read a PBM image and produce a GraphOn terminal raster file
**	
**	Rev 1.1 was based on pbmtolj.c
**
**	Bo Thide', Swedish Institute of Space Physics, bt@irfu.se
**				   
**
** $Log:	pbmtogo.c,v $
 * Revision 1.5  89/11/25  00:24:12  00:24:12  root (Bo Thide)
 * Bug found: The byte after 64 repeated bytes sometimes lost. Fixed.
 * 
 * Revision 1.4  89/11/24  14:56:04  14:56:04  root (Bo Thide)
 * Fixed the command line parsing since pbmtogo now always uses 2D
 * compression.  Added a few comments to the source.
 * 
 * Revision 1.3  89/11/24  13:43:43  13:43:43  root (Bo Thide)
 * Added capability for > 63 repeated bytes and > 62 repeated lines in
 * the 2D compression scheme.
 * 
 * Revision 1.2  89/11/15  01:04:47  01:04:47  root (Bo Thide)
 * First version that works reasonably well with GraphOn 2D runlength
 * encoding/compression.
 * 
 * Revision 1.1  89/11/02  23:25:25  23:25:25  root (Bo Thide)
 * Initial revision
 * 
**
** Copyright (C) 1988, 1989 by Jef Poskanzer, Michael Haberler, and Bo Thide'.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation.  This software is provided "as is" without express or
** implied warranty.
*/

#include <stdio.h>
#include <string.h>

#include "pm_c_util.h"
#include "pbm.h"

#define BUFSIZE 132	/* GraphOn has 132 byte/1056 bit wide raster lines */
#define REPEAT_CURRENT_LINE_MASK	0x00 
#define SKIP_AND_PLOT_MASK		0x40 
#define REPEAT_PLOT_MASK		0x80 
#define PLOT_ARBITRARY_DATA_MASK	0xc0 
#define MAX_REPEAT 64

static unsigned char *scanlineptr;		/* Pointer to current scan line byte */

static void putinit ARGS(( void ));
static void putbit ARGS(( bit b ));
static void putrest ARGS(( void ));
static void putitem ARGS(( void ));

int
main( argc, argv )
     int argc;
     char* argv[];
{
    FILE* ifp;
    bit* bitrow;
    register bit* bP;
    int argn, rows, cols, format, rucols, padright, row, col;
    int nbyte, bytesperrow, ecount, ucount, nout, i, linerepeat;
    int	olditem;
    unsigned char oldscanline[BUFSIZE];
    unsigned char newscanline[BUFSIZE];
    unsigned char diff[BUFSIZE];
    unsigned char buffer[BUFSIZE];
    unsigned char outbuffer[2*(BUFSIZE+1)];	/* Worst case.  Should malloc */
    const char* usage = "[-c] [pbmfile]";


    pbm_init( &argc, argv );

    argn = 2;

    /* Check for flags. */
    if (argc > argn + 1)
      pm_usage(usage);

    if (argc == argn)
      ifp = pm_openr( argv[argn-1] );
    else
      ifp = stdin;

    pbm_readpbminit(ifp, &cols, &rows, &format);

    if (cols > 1056)
        pm_error("Image is wider (%u pixels) than a Graphon terminal "
                 "(%u pixels)", cols, 1056);

    bitrow = pbm_allocrow(cols);

    /* Round cols up to the nearest multiple of 8. */
    rucols = ( cols + 7 ) / 8;
    bytesperrow = rucols;       /* GraphOn uses bytes */
    rucols = rucols * 8;
    padright = rucols - cols;

    for (i = 0; i < BUFSIZE; ++i )
      buffer[i] = oldscanline[i] = 0;
    putinit();

    /* Start donwloading screen raster */
    printf("\033P0;1;0;4;1;%d;%d;1!R1/", rows, rucols);

    linerepeat = 63; /*  63 means "Start new picture" */
    nout = 0;  /* To prevent compiler warning */
    for (row = 0; row < rows; row++) {
        /* Store scan line data in the new scan line vector */
        scanlineptr = newscanline;
        pbm_readpbmrow(ifp, bitrow, cols, format);
        /* Transfer raster graphics */
        for (col = 0, bP = bitrow; col < cols; col++, bP++)
          putbit(*bP);
        for (col = 0; col < padright; col++)
          putbit(0);
        
        /* XOR data from the new scan line with data from old scan line */
        for (i = 0; i < bytesperrow; i++)
          diff[i] = oldscanline[i]^newscanline[i];
        
        /*
         ** If the difference map is different from current internal buffer,
         ** encode the difference and put it in the output buffer. 
         ** Else, increase the counter for the current buffer by one.
         */
        
        if ((memcmp(buffer, diff, bytesperrow) != 0) || (row == 0)) {
            /*    
             **Since the data in the buffer has changed, send the
             **scan line repeat count to cause the old line(s) to
             **be plotted on the screen, copy the new data into
             **the internal buffer, and reset the counters.  
             */
              
            putchar(linerepeat);
            for (i = 0; i < bytesperrow; ++i)
              buffer[i] = diff[i];
            nbyte = 0;          /* Internal buffer byte counter */
            nout = 0;           /* Output buffer byte counter */
              
            /* Run length encode the new internal buffr (= difference map) */
            while (TRUE) {
                ucount = 0;     /* Unique items counter */
                do              /* Find unique patterns */
                  {
                      olditem = buffer[nbyte++];
                      ucount++;
                  } while (nbyte < bytesperrow && (olditem != buffer[nbyte])
                           && (ucount < MIN(bytesperrow, MAX_REPEAT)));
                  
                if ((ucount != MAX_REPEAT) && (nbyte != bytesperrow)) {
                    /* Back up to the last truly unique pattern */
                    ucount--;
                    nbyte--;
                }

                if (ucount > 0) { 
                    /* Output the unique patterns */
                    outbuffer[nout++] = 
                      (ucount-1) | PLOT_ARBITRARY_DATA_MASK;
                    for (i = nbyte-ucount; i < nbyte; i++)
                      outbuffer[nout++] = buffer[i];
                }

                /*
                 ** If we already are at the end of the current scan
                 ** line, skip the rest of the encoding and start
                 ** with a new scan line.  
                 */

                if (nbyte >= bytesperrow)
                  goto nextrow;
                  
                ecount = 0;     /* Equal items counter */
                do              /* Find equal patterns */
                  {
                      olditem = buffer[nbyte++];
                      ecount++;
                  } while (nbyte < bytesperrow && (olditem == buffer[nbyte])
                           && (ecount < MIN(bytesperrow, MAX_REPEAT)));
                  
                if (ecount > 1) {
                    /* More than 1 equal pattern */
                    if (olditem == '\0') {
                        /* White patterns */
                        if (nbyte >= bytesperrow-1) {
                            /* No more valid data ahead */
                            outbuffer[nout++] = 
                              (ecount-2) | SKIP_AND_PLOT_MASK;
                            outbuffer[nout++] = buffer[nbyte-1];
                        } 
                        else { 
                            /* More valid data ahead */
                            outbuffer[nout++] = 
                              (ecount-1) | SKIP_AND_PLOT_MASK;
                            outbuffer[nout++] = buffer[nbyte++];
                        } 
                    }
                    else { 
                        /* Non-white patterns */
                        outbuffer[nout++] = (ecount-1) | REPEAT_PLOT_MASK;
                        outbuffer[nout++] = olditem;
                    } /* if (olditem == '\0') */
                } /* if (ecount > 1) */
                else
                  nbyte--;      /* No equal items found */
                  
                if (nbyte >= bytesperrow)
                  goto nextrow;
            } /* while (TRUE) */
              
          nextrow: printf("%d/", nout+1); /* Total bytes to xfer = nout+1 */
            fflush(stdout);

            /* Output the plot data */
            write(1, outbuffer, nout);

            /* Reset the counters */
            linerepeat = 0;
        } else {
            linerepeat++;
            if (linerepeat == 62) /* 62 lines max, then restart */
              {
                  putchar(linerepeat);
                  printf("%d/", nout+1);
                  fflush(stdout);
                  write(1, outbuffer, nout);
                  linerepeat = 0;
              }
        }
        
        /* Now we are ready for a new scan line */
        for (i = 0; i < bytesperrow; ++i)
          oldscanline[i] = newscanline[i];
    }
    putchar(linerepeat);        /* For the last line(s) to be plotted */
    pm_close(ifp);
    putrest();
    exit(0);
}



static int item, bitsperitem, bitshift;

static void
putinit()
{
  /* Enter graphics window */
  printf("\0331");

  /* Erase graphics window */
  printf("\033\014");

  /* Set graphics window in raster mode */
  printf("\033r");

  /* Select standard Tek coding **/
  printf("\033[=11l");

  bitsperitem = 1;
  item = 0;
  bitshift = 7;
}

#if __STDC__
static void
putbit(bit b)
#else /*__STDC__*/
static void
putbit(b)
bit b;
#endif /*__STDC__*/
{
  if (b == PBM_BLACK)
    item += 1 << bitshift;
  bitshift--;
  if (bitsperitem == 8)
  {
    putitem();
    bitshift = 7;
  }
  bitsperitem++;
}

static void
putrest()
{
  if (bitsperitem > 1)
      putitem();

  /* end raster downloading */
  printf("\033\134");

  /* Exit raster mode */
  printf("\033t");

  /* Exit graphics window
  printf("\0332"); */
}

static void
putitem()
  {
  *scanlineptr++ = item;
  bitsperitem = 0;
  item = 0;
  }
