/*
   *-----------------*-----------------------------------------------*
   | John S. Urban   |     Westinghouse Electric Corporation         |
   |  WIN 284-4568   |  Westinghouse Corporate Computer Services     |
   | (412)374-4568   |  CRAY/Unix Development, Technical Services    |
   *-----------------*-----------------*-----------*-----------------*
   | Westinghouse Electric Corporation | FAX: 6924 |                 |
   |       Post Office Box 355         | MGR: 5826 |                 |
   |     Pittsburgh, PA 15230-0355     | SECY:6861 |                 |
   *-----------------------------------*-----------*-----------------*
           Mail messages to urbanjs@daisy.pgh.wec.com
   			 urban.j.s@mts400.pgh.wec.com
   
     Call WCCS Service Center (WIN 284-6100 or (412)374-6100) for
                        general assistance.
   
   Sun Jan 21 02:10:02 EST 1996

I have a limited driver that directly makes  pbmplus-readable (P1 and P4
format)  monochrome  pixmaps  and an X11 xbm  bitmap  file  (large,  but
portable and readable by most WWW browsers).

I keep on  planning  on doing a lot more with it and never  do.  Some of
the  changes  needed  are  down-right  straight-forward  (like  allowing
alternate  sizes).  I got it to make what I needed the first  night, and
never  got  back to it.  Even  though  it is  somewhat  embarrassing  to
release this as-is, I believe some of you will find it valuable.

Though  limited  (uses  only  software  fonts,  single  line  thickness,
monochrome, has around two machine  dependencies ...  ) I find it useful
for making  icons,  printing  to a COM unit that can print  only  raster
images and text,  making  on-the-fly  graphics for WWW pages, and so on.
If you have the  pbmplus  and xv packages  you'll  find it more  useful.

Since vogle provides so much higher-level  function it's surprising what
you can do with a driver  that makes  single-frame  files in  monochrome
with one line  style.  The tek 4014 driver  really  doesn't do much more
than that, after all. 

I use an old version of vogle, but I think any changes should be trivial
for anyone on a 32-bit machine with  high-to-low  endian storage.  I use
it on HP-UX and SunOS, and on ULTRIX with a few tweeks to handle storage
order.

Please pass any upgrades back to me ( urban.j.s@mts400.pgh.wec.com  ) if
you get a chance.

PS:

If the upside-down  part seems odd, remember that I got vogle from "down
under" and maybe you'll smile about it too.

And if you have a newer  version  of xv(1)  from the 'Net try the emboss
algorithm  on a bitmap.  Might not be all that  functional,  but cute as
they come.
*/

/* PBM/X11 Bitmap Driver for vogle; Version 1.0, John S. Urban, Jan 1995  */

/* References: 1) Chapter 10, Practical C Programming, Steve Oualline, O'Reilly & Associates, Inc. 1991
               2) Fundamentals of Interactive Computer Graphics, Foley & Van Dam, Addison Wesley Publishing Company, 1982
               3) pbm - portable bitmap file format, 27 September 1991, Copyright (C) 1989, 1991 by Jef Poskanzer.
/*
 From Reference 1:
    In bitmapped  graphics, each pixel on the screen is represented by a
    single bit in memory.  There is no data type for an array of bits in
    C.  The closest we can come is an array of bytes.

    This macro  turns on the bit  (pixel)  located at (x,y).  We need to
    compute two  values:  the  coordinate  of the byte and the number of
    the bit  within  the byte.  Our bit  address  is  (x,y).  Bytes  are
    groups of eight bits so our bit address is (x/8,y).

    The bit  within  the byte is not so  simple.  We want to  generate a
    mask  consisting of the single bit we want to set.  For the leftmost
    bit, this should be 10000000#2 or 80#16.  This occurs when (x%8)==0.
    The next bit is  01000000#2 or (0x80>>1)  and occurs when  (x%8)==1.
    So  in  order  to  generate  our  bitmask,  we  use  the  expression
    (0x80>>(x%8)).

    Now that we have the byte  location and the bit mask, all we have to
    do is set the bit.  The  following  macro  will set a given bit in a
    bitmapped  graphics array named  graphics:

*/
#define  SET_BIT(x,y) graphics[ (x)/8] [y] |= (0x80 >> ((x)%8))

/*
    NEXT TIME IN:
    o  allocate memory and release it for any size, obey prefsize and prefposition
    o  line thickness
    o  probably overindex graphics array when clipping is off
    o  write X11 xbm (readable by xv and xsetroot) format, X11 atobm format
       depending on ending given to voutput (precludes using stdout) or
       based on a UNIX environmental variable
    o  color and grayscale Poskanzer formats
    o  polygon fill (use hatching in the mean time)
    o  machine-independent for low endian, high endian, and word size
    o  allow preloading a P1 or P4 file into the "background" array
    o  clear memory to get ready for next output
    o  what about multi-image files?
    o  make it so a "page" goes to next chunk, allowing rows x columns
    o  what to do about hardware text
    o  could keep track of maximum value used, and only write out to there
    o  option to reverse black and white bit
    o  straight to JPEG for WWW browser
    o  add an emboss  feature like recent  versions of xv use.  Does pbm
       stuff have a similiar algorithm?  If make one, have a raise/lower
       switch (reverse video would work for grayscale output). Probably
       a convolution of a grayscale would work.
 */

#define byte unsigned char

#define X_SIZE 600 /* size of array in the X direction */
#define Y_SIZE 400 /* size of array in the Y direction */

/* We use X_SIZE and Y_SIZE/8 since we pack 8 bits per byte */
unsigned char graphics[(X_SIZE + 7 ) / 8] [Y_SIZE]; /* the graphics data */

#define XBM 1
#define P1  2
#define P4  3
static int DRIVER = 0;
/******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "vogle.h"
#define MIN(x,y)        ((x) < (y) ? (x) : (y))

extern FILE	*_voutfile();

#define UNDRAWN 0
#define DRAWN   1
static int drawn = UNDRAWN;

static int	plotlstx, plotlsty;	/* position of last draw */

static FILE	*fp;
/******************************************************************************/
static int noop() { return(-1); } /* do nothing but return-1 */
/******************************************************************************/
static int PBM_init()
{
        int prefx, prefy, prefxs, prefys;
        getprefposandsize(&prefx, &prefy, &prefxs, &prefys);
	if (prefxs != -1 ) {
	   vdevice.sizeSy = prefys; 
	   vdevice.sizeSx = prefxs; 
	}else{
	   prefx = 0;
	   prefy = 0;
	   vdevice.sizeSy = Y_SIZE;
   	   vdevice.sizeSx = X_SIZE;
	}


	vdevice.sizeX = vdevice.sizeY = MIN(vdevice.sizeSy,vdevice.sizeSx);

	vdevice.depth = 1;

	fp = _voutfile();

	/* * Cause scaling to be 0 to maxX maxY: prefx, vdevice.sizeSx+prefx, prefy, vdevice.sizeSy+prefy */

	plotlstx = -1111111;
	plotlsty = -1111111;

        drawn = UNDRAWN;

	return(1);
}
/******************************************************************************/
static PBM_draw(x, y) /* print the commands to draw a line from the current graphics position to (x, y).  */
	int	x, y;
{
	if (plotlstx != vdevice.cpVx || plotlsty != vdevice.cpVy){
		plotlstx=vdevice.cpVx;
		plotlsty=vdevice.cpVy;
	}

	/* fprintf(stderr,"\n LINE: %d %d %d %d",plotlstx,plotlsty,x,y); */
        PBM_draw_line(x,y);
	drawn = DRAWN;
}
/******************************************************************************/
static PBM_exit() /* exit from vogle printing the command to flush the buffer.  */
{
	if( drawn ){
		switch(DRIVER) {
		case P1:
			P1_print_graphics();
			break;
		case P4:
			P4_print_graphics();
			break;
		case XBM:
			XBM_print_graphics();
			break;
		default:
			fprintf(stderr, "pbm driver: UNKNOWN DRIVER NAME\n");
			P1_print_graphics();
		}
	}
	fflush(fp); /* flush the output file */

	if (fp != stdout){
		fclose(fp);
	}
}
/******************************************************************************/
static PBM_clear() /* flush current page without resetting graphics state of printer */
{
        if (drawn){
		switch(DRIVER) {
		case P1:
			P1_print_graphics();
			break;
		case P4:
			P4_print_graphics();
			break;
		case XBM:
			XBM_print_graphics();
			break;
		default:
			fprintf(stderr, "pbm driver: UNKNOWN DRIVER NAME\n");
			P1_print_graphics();
		}
	}
	memset(graphics, 0, sizeof(graphics)); /* reset graphics to all zero */
}
/******************************************************************************/
static int PBM_font(font) /* load in large or small */
	char	*font;
{
 	fprintf(stderr, "E-R-R-O-R: NO HARDWARE FONT\n");
	if (strcmp(font, "small") == 0) {
		vdevice.hwidth = 97.01;	/* Size in plotter resolution units */
		vdevice.hheight = vdevice.hwidth * 2.0;
	} else if (strcmp(font, "large") == 0) {
		vdevice.hwidth = 145.5;
		vdevice.hheight = vdevice.hwidth * 2.0;
	} else 
		return(0);

	return(1);
}
/******************************************************************************/
static PBM_char(c) /* PBM_char output a character */
char    c;
{
  char  s[2];
  s[0] = c; s[1]='\0';
  PBM_string(s);
}
/******************************************************************************/
static PBM_string(s) /* output a string.  */
	char	*s;
{
	int		dy, dx;

	if (plotlstx != vdevice.cpVx || plotlsty != vdevice.cpVy){
		plotlstx=vdevice.cpVx;
		plotlsty=vdevice.cpVy;
	}

	fputs(s, fp);

	plotlstx = plotlsty = -1111111; /* undefine current position because used hardware text ?*/
        drawn = DRAWN;
}
/******************************************************************************/
static PBM_fill(n, x, y) /* "fill" a polygon */
	int     n, x[], y[];
{
	int     i;

       /* update current position if needed */
		plotlstx=x[0];
		plotlsty=y[0];

	for (i = 1; i < n; i++){
        	PBM_draw_line(x[i],y[i]); /* draws a diagonal line across a graphics array */
	}

       /* update current position */
	plotlstx = vdevice.cpVx = x[n - 1];
	plotlsty = vdevice.cpVy = y[n - 1];

        drawn = DRAWN;
}
/******************************************************************************/
static DevEntry PBMdev = {
                "PBM",       /* name of device */
                "large",     /* name of large font */
                "small",     /* name of small font */
                noop,        /* Set drawing in back buffer */
                PBM_char,    /* Draw a hardware character */
                noop,        /* Check if a key was hit */
                PBM_clear,   /* Clear the screen to current color */
                noop,        /* Set current color */
                PBM_draw,    /* Draw a line */
                PBM_exit,    /* Exit graphics */
                PBM_fill,    /* Fill a polygon */
                PBM_font,    /* Set hardware font */
                noop,        /* Set drawing in front buffer */
                noop,        /* Wait for and get the next key hit */
                PBM_init,    /* Initialise the device */
                noop,        /* Get mouse/cross hair position */
                noop,        /* Set color indicies */
                PBM_string,  /* Draw a hardware string */
                noop,        /* Swap front and back buffers */
                noop         /* Syncronise the display */
};
/******************************************************************************/
_PBM_devcpy()
{
	vdevice.dev = PBMdev;
	vdevice.dev.Vinit = PBM_init;
        DRIVER = P1;
}
/******************************************************************************/
_XBM_devcpy()
{
	vdevice.dev = PBMdev;
	vdevice.dev.Vinit = PBM_init;
        DRIVER = XBM;
}
/******************************************************************************/
_P4_devcpy()
{
	vdevice.dev = PBMdev;
	vdevice.dev.Vinit = PBM_init;
        DRIVER = P4;
}
/*******************************************************************************/
static P1_print_graphics() /* print_graphics -- print the graphics bit array as a pbm P1 file*/
{
	int x; /* current x BYTE */
	int y; /* current y location */
	int bit; /* bit we are testing in the current byte */
	int column;
	int write_column = 1;

        (void) fprintf(fp,"P1\n"); /* magic number of a pbm file */
        (void) fprintf(fp,"# CREATOR: VOGLE pbm driver; version 1.0 1995/01/09\n"); /*pbm P1 file can contain comment lines*/
        (void) fprintf(fp,"# UnCopyright (C) 1995, John S. Urban\n"); 
        (void) fprintf(fp,"%d %d\n",X_SIZE,Y_SIZE); /* size of bitmap */

	/* notice going from bottom to top because putting out in a right handed coordinate system, was assuming left-handed */
	for (y = (Y_SIZE-1); y >= 0; y--) {
		/* Loop for each byte in the array */
		column=1; /* count column because pbm P1 files do not have to be multiples of 8 bits wide */
		for ( x = 0; x <(X_SIZE + 7) / 8; x++){
			/* Handle each bit */
			for (bit = 0x80; bit > 0;bit = (bit >>1)) {
				/* file is not multiple of 8 bits wide (but storage array is) and came to last significant column */
				if (column > X_SIZE)
					break;
				/* output a character representing the bit */
				if ((graphics[x][y] & bit) != 0)
					(void) fprintf(fp,"1");
				else
					(void) fprintf(fp,"0");
				/* increment the dataset column counter */
				column++;

				/* The manual says a P1 pbm file should not be wider than 70 characters */
				write_column++;
				if ( write_column > 70 )
				{
					(void) fprintf(fp,"\n");
					write_column = 1;
				}
			}
		} /* end of writing a row */
	} /* end of writing a column */
	(void) fprintf(fp,"\n");
	drawn = UNDRAWN;
}
/*******************************************************************************/
static P4_print_graphics() /* print_graphics -- print the graphics bit array as a pbm P4 file*/
{
	int x; /* current x BYTE */
	int y; /* current y location */

	(void) fprintf(fp,"P4\n"); /* magic number of a pbm file */
        (void) fprintf(fp,"# CREATOR: VOGLE pbm driver; version 1.0 1995/01/09\n"); /*pbm P4 file can contain comment lines*/
        (void) fprintf(fp,"# Copyright (C) 1995, John S. Urban\n"); 
	(void) fprintf(fp,"%d %d\n",X_SIZE,Y_SIZE); /* size of bitmap */

	/* notice going from bottom to top because putting out in a right handed coordinate system, was assuming left-handed */
	for (y = (Y_SIZE-1); y >= 0; y--) {
		for ( x = 0; x <(X_SIZE + 7) / 8; x++){
			putc((char)graphics[x][y],fp);
		}
	}
	drawn = UNDRAWN;
}
/*******************************************************************************/
static XBM_print_graphics() /* print graphics bitmap as an X11 bitmap file */
{
	/* graphics is a byte array whose width is a multiple of 8 */

	int   x;
	int   y;
	int   print_column;
	int   nbytes;
        /* ENDIAN char* hexchar = "0123456789abcdef"; */ 
        char* hexchar = "084c2a6e195d3b7f";
	byte  item;

	/* should probably use filename to build strings */
	fprintf(fp,"#define VOGLE_width %d\n",X_SIZE);
	fprintf(fp,"#define VOGLE_height %d\n",Y_SIZE);
	fprintf(fp,"static char VOGLE_bits[] = {\n ");

	nbytes = Y_SIZE * ((X_SIZE + 7)/8);   /* number of bytes to write */

	print_column=1;
	for (y = (Y_SIZE-1); y >= 0; y--) {
		for ( x = 0; x <(X_SIZE + 7) / 8; x++,nbytes--){
			/* fprintf(fp,"0x%02x,",(byte)graphics[x][y] );*/
                        putc('0',fp); 
                        putc('x',fp);
			item = graphics[x][y];
			/*
			   cute trick that I probably will not remember for 
			   writing hex representation of the bitmap in order
			   desired by X11 bitmap file
			*/
                        putc(hexchar[item & 15],fp);
                        putc(hexchar[item >> 4],fp);
			/* ENDIAN
                        putc(hexchar[item & 15],fp);
                        putc(hexchar[item >> 4],fp);
			*/
			print_column += 5;
			if (print_column>75){
				putc(',',fp);
				putc('\n',fp);
				putc(' ',fp);
				print_column=1;
			}else{
				putc(',',fp);
			}
			if(nbytes == 2)
				exit; /* special rules for printing last byte */
		}
	}
	fprintf(fp,"0x%02x\n};",(byte) graphics[x][y]); /* last byte should not be followed by a comma */
}
/*******************************************************************************/
static PBM_draw_line(x,y) /* draws a line across a graphics array */
int	x, y;
{
	int runcount;
	int dx,dy;
	int xinc,yinc;
	int xplot,yplot;

	SET_BIT(plotlstx,plotlsty); /* move to initial spot */

	runcount=0;

	dx = abs(plotlstx-x);

	if (x > plotlstx)  xinc=  1;
	if (x == plotlstx) xinc=  0;
	if (x < plotlstx)  xinc= -1;

	dy = abs(plotlsty-y);

	if (y > plotlsty)  yinc=  1;
	if (y == plotlsty) yinc=  0;
	if (y < plotlsty)  yinc= -1;

	xplot = plotlstx;
	yplot = plotlsty;
	
	if (dx>dy) {
		/* iterate x */
		while (xplot != x) {
			xplot += xinc;
			runcount += dy;
			if (runcount >= (dx-runcount)) {
				yplot += yinc;
				runcount -= dx;
			}
			SET_BIT(xplot,yplot);
		}
	} else {
		/* iterate y */
		while (yplot != y) {
			yplot += yinc;
			runcount += dx;
			if (runcount >= (dy-runcount)) {
				xplot += xinc;
				runcount -= dy;
			}
			SET_BIT(xplot,yplot);
		}
	}
	plotlstx = xplot;
	plotlsty = yplot;
	return(0);
}

