[comp.sources.sun] v01i052: Xbgsun - Display sunraster files in X

mcgrew@dartagnan.rutgers.edu (Charles Mcgrew) (07/21/89)

Submitted-by: madd@bu-it.bu.edu
Posting-number: Volume 1, Issue 52
Archive-name: xbgsun

#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	README
#	common.c
#	common.h
#	patchlevel.h
#	rasterfile.h
#	xbgsun.c
#	xbgsun.man
#	xviewsun.c
#	xviewsun.man
# This archive created: Fri Jun 16 09:44:26 1989
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
	echo shar: over-writing existing file "'Makefile'"
fi
cat << \SHAR_EOF > 'Makefile'
# Makefile for xbgsun & xviewsun
#
# jim frost 12.19.88
#
# -DPATH should indicate the local image directory if one exists.
# -DSUFFIX indicates a default suffix for images if desired.  if
# images are compressed, the suffix should NOT include the .Z since .Z
# is automatically appended if no uncompressed file is found.
#
# Makefile for xviewsun
#
OPTIONS= -DPATH=\"/global/bitmaps\" -DSUFFIX=\".sun\"
CFLAGS= -O $(OPTIONS) -I/global/include
CC= cc
LDFLAGS=-L/global/lib
LIBS= -lX11

all: xviewsun xbgsun 

xviewsun: common.o xviewsun.o
	$(CC) $(LDFLAGS) -o xviewsun $(CFLAGS) xviewsun.o common.o $(LIBS)

xbgsun: common.o xbgsun.o
	$(CC) $(LDFLAGS) -o xbgsun $(CFLAGS) xbgsun.o common.o $(LIBS)

.c.o:
	$(CC) $(CFLAGS) -c $*.c

common.o: common.h


clean:
	rm -f xviewsun xviewsun.o xbgsun xbgsun.o common.o
SHAR_EOF
if test -f 'README'
then
	echo shar: over-writing existing file "'README'"
fi
cat << \SHAR_EOF > 'README'
This package contains two programs, xbgsun and xviewsun, which are
used to manipulate Sun rasterfile images under the X environment.

Xbgsun will load one or more Sun rasterfiles onto the X root window.
It can optionally perform some kinds of manipulations on the images.

Xviewsun will show a Sun rasterfile in an X window.

Both xbgsun and xviewsun can handle monochrome, color, and run-length
encoded rasterfiles, and can use rasterfiles of different depths than
the destination display.  If the destination display has fewer colors
than the rasterfile, they will attempt to pick the closest available
color.

See the enclosed man pages for details on operation.

Jim Frost
Associative Design Technology
madd@bu-it.bu.edu
01.06.88
SHAR_EOF
if test -f 'common.c'
then
	echo shar: over-writing existing file "'common.c'"
fi
cat << \SHAR_EOF > 'common.c'
/* common.c:
 *
 * routines common to both xbgsun and xviewsun
 *
 * jim frost 01.06.88
 *
 * this is in the public domain
 *
 * 01.12.88 fix bug in calculation of bytesperline.
 * 01.06.88 ability to handle run-length encoding added.
 * 12.30.88 original version derived from xbgsun.
 */

#include <stdio.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <X11/Xlib.h>
#include "rasterfile.h"

/* global variables.  we could just pass these around but this is simpler
 * since there are so many.
 */

Display           *disp;                /* X display */
int               width, height, depth; /* dimensions of image */
int               xorigin, yorigin;     /* where to put image */
int               pwidth, pheight;      /* dimensions of pixmap */
int               cx, cy;               /* clip origin */
int               cwidth, cheight;      /* clip dimensions */
int               verbose;              /* talkative mode flag */
int               docorrupt;            /* load corrupt image flag */
char              *fgcolor, *bgcolor;   /* foreground/background colors */
Pixmap            pic;                  /* destination pixmap */
XWindowAttributes wa;                   /* root window attributes */

/* local globals
 */

static unsigned char *data;                /* image data area */
static unsigned long *pixdata;             /* pixel data area */
static unsigned char *red, *blue, *green;  /* colormap data areas */
static int           mapsize;              /* number of colors in colormap */
static int           bytesperline;         /* bytes per image line */
static int           imagebyte, linebyte;

/* return true if option matches argument for a minimum number of chars
 */

int isoption(op, arg, min)
char *op, *arg;
int min;
{
  if ((strlen(arg) < min) && !strncmp(op, arg, strlen(arg))) {
    printf("not enough characters specified on option '%s'\n", arg);
    exit(1);
  }
  return(!strncmp(op, arg, strlen(arg)));
}

/* memToVal and valToMem convert 68000-ordered numbers into whatever the
 * local ordering is.  if you have bit or byte ordering problems, fix
 * them here.  this was tested on machines with differing bit and byte
 * orders so it should work fine.  your mileage may vary.
 */

static unsigned int memToVal(d, l)
unsigned char *d; /* char array of number */
int      l;       /* length of array */
{ int a;
  unsigned int i;

  i= 0;
  for (a= 0; a < l; a++)
    i= (i << 8) + *(d++);
  return(i);
}

static void valToMem(d, l, v)
unsigned char *d;
int l;
unsigned long v;
{ int a;

  for (a= l - 1; a >= 0; a--) {
    *(d + a)= v & 0xff;
    v >>= 8;
  }
}

/* macro to do 68000 to local integer conversions
 */

#define localint(d) memToVal(d, 4)

/* this attempts to find the image.   order:
 *   name
 *   name.Z
 *   nameSUFFIX
 *   nameSUFFIX.Z
 *   PATH/name
 *   PATH/name.Z
 *   PATH/nameSUFFIX
 *   PATH/nameSUFFIX.Z
 */

static char *findImage(name)
char *name;
{ static char fname[BUFSIZ];
  struct stat sbuf; /* dummy */

  if (!stat(name, &sbuf))
    return(name);
  sprintf(fname, "%s.Z", name);
  if (!stat(fname, &sbuf))
    return(fname);
#ifdef SUFFIX
  sprintf(fname, "%s%s", name, SUFFIX);
  if (!stat(fname, &sbuf))
    return(fname);
  sprintf(fname, "%s%s.Z", name, SUFFIX);
  if (!stat(fname, &sbuf))
    return(fname);
#endif
#ifdef PATH
  if ((*name == '.') || (*name == '/'))
    return(name);
  sprintf(fname, "%s/%s", PATH, name);
  if (!stat(fname, &sbuf))
    return(fname);
  sprintf(fname, "%s/%s.Z", PATH, name);
  if (!stat(fname, &sbuf))
    return(fname);
#ifdef SUFFIX
  sprintf(fname, "%s/%s%s", PATH, name, SUFFIX);
  if (!stat(fname, &sbuf))
    return(fname);
  sprintf(fname, "%s/%s%s.Z", PATH, name, SUFFIX);
  if (!stat(fname, &sbuf))
    return(fname);
#endif
#endif
  return(name);
}

/* this is called if there is a read error getting the image
 */

static int badRead(fname)
char *fname;
{
  if (docorrupt) {
    printf("%s: read problem, attempting to use what I have\n", fname);
    return(0);
  }	
  printf("%s: error reading image data (possibly short image)\n", fname);
  return(-1);
}

/* read in run length encoded data and decode it
 */

static int readEncoded(f, buf, size)
     FILE          *f;
     unsigned char *buf;
     int           size;
{ static int           remaining= 0;
  static unsigned char repeating;

  while (size--)
    if (remaining) {
      remaining--;
      *(buf++)= repeating;
    }
    else {
      if (fread(&repeating, 1, 1, f) != 1)
	return(-1);
      if (repeating == RESC) {
	if (fread(&repeating, 1, 1, f) != 1)
	  return(-1);
	if (repeating == 0)
	  *(buf++)= RESC;
	else {
	  remaining= repeating;
	  if (fread(&repeating, 1, 1, f) != 1)
	    return(-1);
	  *(buf++)= repeating;
	}
      }
      else
	*(buf++)= repeating;
    }
}

/* this loads the rasterfile into memory
 */

int loadImage(name)
     char *name;
{ FILE           *f;
  char           *fname, cmd[BUFSIZ];
  struct rheader header;
  unsigned char  *colormap, byte;
  int            a, b, len, ilen, maplen, rlencoded;

  /* get rasterfile; we get it from uncompress -c if it ends in .Z, or
   * look for a .Z if we don't find an uncompressed one.
   */

  fname= findImage(name);
  if ((strlen(fname) > 2) && (!strcmp(fname + strlen(fname) - 2, ".Z"))) {
    sprintf(cmd, "uncompress -c %s", fname);
    f= popen(cmd, "r");
  }
  else
    f= fopen(fname, "r");

  if (f == NULL) {
    perror(fname);
    return(-1);
  }

  if (fread(&header, sizeof(struct rheader), 1, f) != 1) {
    printf("%s: error loading rasterfile header.\n", fname);
    return(-1);
  }

  /* check magic number
   */

  if (localint(header.magic) != RMAGICNUMBER) {
    printf("I don't know what '%s' is, but it's not a Sun raster image.\n",
	   fname);
    return(-1);
  }

  /* filter out unsupported rasterfiles
   */

  switch(localint(header.type)) {
  case RSTANDARD :
    rlencoded= 0;
    break;
  case RRLENCODED :
    rlencoded= 1;
    break;
  default :
    printf("%s: unsupported rasterfile type\n", fname);
    return(-1);
  }

  if ((localint(header.maptype) != RNOMAP) &&  /* no map, no problem */
      (localint(header.maptype) != RRGBMAP)) {
    printf("%s: unsupported colormap type\n", fname);
    return(-1);
  }

  width= localint(header.width);
  height= localint(header.height);
  depth= localint(header.depth);

  if (verbose) {
    printf("Hmm, %s is a %dx%d %s image", name, width, height,
	   (depth == 1 ? "monochrome" : "color"));
    if (depth > 1)
      printf(" with %d planes", depth);
    printf(".\n");
  }

  /* read the colormap
   */

  if ((maplen= localint(header.maplen)) > 0) {
    if ((colormap= (unsigned char *)malloc(maplen)) == NULL) {
      printf("%s: malloc error (cannot load colormap)\n", fname);
      exit(1);
    }
    if (fread(colormap, maplen, 1, f) != 1) {
      printf("%s: error reading colormap\n", fname);
      return(-1);
    }
  }

  bytesperline= ((width * depth) / 16); /* images are rounded to 16 bits */
  if ((width * depth) % 16)
    bytesperline++;
  bytesperline <<= 1;

  if (depth) {
    mapsize= localint(header.maplen) / 3;
    red= colormap;
    green= colormap + mapsize;
    blue= colormap + (mapsize * 2);
  }
  else {
    mapsize= 0;
    red= green= blue= NULL;
  }

  /* load image data
   */

  if ((data= (unsigned char *)malloc(height * bytesperline)) == NULL) {
    printf("%s: malloc error (cannot load image)\n", fname);
    exit(1);
  }

  for (a= 0; a < height; a++)
    if (rlencoded) {
      if (readEncoded(f, data + (a * bytesperline), bytesperline) < 0)
	return(badRead(fname));
    }
    else
      if (fread(data + (a * bytesperline), bytesperline, 1, f) != 1)
	return(badRead(fname));
  return(0);
}

/* convert a color name to a pixel value
 */

unsigned long nameToPixel(name, pixel)
    char          *name;
    unsigned long *pixel;
{ XColor color;

  if (!XParseColor(disp, wa.colormap, name, &color)) {
    printf("Unknown color '%s'", name);
    return(-1);
  }
  if (!XAllocColor(disp, wa.colormap, &color)) {
    printf("Cannot allocate color '%s'", name);
    return(-1);
  }
  *pixel= color.pixel;
  return(0);
}

/* find the best color in our colormap
 */

static void findBestColor(xcolor)
XColor *xcolor;
{ XColor qcolor;
  int    a;
  int    bcolor; /* best color */
  long   dist;   /* our distance from the color */
  long   bdist;  /* distance for best color yet */
  long   qdist;

  bdist= 256 * 256 * 3;
  xcolor->red >>= 8;   /* shifted so the distance value will fit into */
  xcolor->green >>= 8; /* a long comfortably.  why use floats? */
  xcolor->blue >>= 8;
  
  dist= (xcolor->red * xcolor->red) + (xcolor->green * xcolor->green) +
    (xcolor->blue * xcolor->blue);
  for (a= 0; a < (1 << wa.depth); a++) {
    qcolor.pixel= a;
    XQueryColor(disp, wa.colormap, &qcolor);
    qcolor.red >>= 8;
    qcolor.green >>= 8;
    qcolor.blue >>= 8;
    qdist= (qcolor.red * qcolor.red) + (qcolor.green * qcolor.green) +
      (qcolor.blue * qcolor.blue) - dist;
    if (qdist < 0)
      qdist= -qdist;
    if (qdist < bdist) {
      bdist= qdist;
      bcolor= a;
    }
  }
  xcolor->pixel= bcolor;
}

/* translate the image into local colors
 */

void translateColors()
{ int           x, y;
  int           pixlen;     /* length of image pixel in bytes */
  char          *haspixval; /* 1 if we've allocated this pixel's color */
  unsigned long *pixval;    /* local pixel for image's pixel value */
  unsigned char *pixrow;    /* start of image row */
  unsigned char *pixloc;    /* current pixel we're playing with */
  unsigned long pixel;      /* actual pixel value from image */
  Colormap      cmap;       /* color map we're using */
  XColor        xcolor;
  int           rval, gval, bval; /* rgb values of image pixel */
  int           colors;     /* number of colors actually allocated */
  unsigned long *dptr;      /* pointer into pixdata */
  int           greyscale;  /* 1 if colormap is greyscale */

  /* in the interest of keeping our normal colormap, we only allocate those
   * pixel values which are actually used in the image and translate the
   * pixel values in the image to those we've allocated.  this can be quite
   * time consuming, but that's the cost of color!
   */

  pixlen= depth >> 3;
  cmap= wa.colormap;
  xcolor.flags= DoRed | DoGreen | DoBlue;
  if (((haspixval= malloc(1 << depth)) == NULL) ||
      ((pixval= (unsigned long *)malloc((1 << depth) * sizeof(long)))
       == NULL)) {
    printf("Malloc failure.\n");
    XCloseDisplay(disp);
    exit(1);
  }

  /* if we're sending to a different depth of display, we must translate
   * the image into pixel values and put it somewhere else.  this grabs
   * the new area.
   */

  if (wa.depth != depth)
    if ((pixdata= dptr= (unsigned long *)malloc(sizeof(long) * width *
						height)) == NULL) {
      printf("Malloc failure (can't allocate pixel data area).\n");
      exit(1);
    }

  greyscale= 1;
  colors= 0;
  for (x= 0; x < (1 << depth); x++) /* none yet */
    *(haspixval + x)= 0;

  for (y= 0; y < height; y++) {
    pixrow= data + (y * bytesperline);
    for (x= 0; x < width; x++) {
      pixloc= pixrow + (x * pixlen);
      pixel= memToVal(pixloc, pixlen);

      if (pixel >= mapsize) {
	printf("Something's bogus -- found a pixel with no colormap entry.\n");
	exit(1);
      }

      if (! *(haspixval + pixel)) {
	rval= memToVal(red + pixel, 1);
	gval= memToVal(green + pixel, 1);
	bval= memToVal(blue + pixel, 1);

	if (greyscale && ((rval != gval) || (rval != bval)))
	  greyscale= 0;

	/* if we're color, grab a new colormap entry
         */

	if (wa.depth > 1) {
	  xcolor.red= rval << 8;     /* X colors are 16 bits */
	  xcolor.green= gval << 8;
	  xcolor.blue= bval << 8;
	  if (!XAllocColor(disp, cmap, &xcolor)) {
	    cmap= XCopyColormapAndFree(disp, cmap);
	    if (!XAllocColor(disp, cmap, &xcolor))
	      findBestColor(&xcolor);
	  }
	}

	/* if we're mono, figure out if the color is whiter or blacker.  most
	 * servers do this automagically in XAllocColor() but often they
	 * don't do as good a job.
	 */

	else {
	  xcolor.pixel= ((rval * rval) + (gval * gval) + (bval * bval));
	  if (xcolor.pixel < ((255 * 255 * 3) - xcolor.pixel))
	    xcolor.pixel= BlackPixel(disp, DefaultScreen(disp));
	  else
	    xcolor.pixel= WhitePixel(disp, DefaultScreen(disp));
	}

        *(haspixval + pixel)= 1;
	*(pixval + pixel)= xcolor.pixel;
	colors++;
      }
      if (wa.depth == depth)
	valToMem(pixloc, pixlen, *(pixval + pixel));
      else
	*(dptr++)= *(pixval + pixel);
    }
  }
  if (verbose) {
    if (greyscale)
      printf("This is a greyscale image.\n");

    if (colors < (1 << depth))
      printf("Image only used %d of %d colors in its colormap.\n", colors,
	     1 << depth);
    else
      printf("Image used all of the colors in its colormap.\n");

    if (wa.depth == 1)
      printf("Monochrome destination -- this is going to be ugly.\n");
    else if (wa.depth < depth)
      printf("Fewer destination than source planes -- could be \
interesting.\n");
  }
  wa.colormap= cmap;
}

/* send across a monochrome image
 */

void sendMonoImage()
{ XImage    *image;   /* XImage for our raster image */
  Pixmap    picplane; /* monochrome plane used in transfer */
  XGCValues gcv;
  GC        gc;
  GC        planegc;  /* gc for picplane */

  /* set up an XImage structure that points to our bitmap data
   */

  image= XCreateImage(disp, DefaultVisual(disp, DefaultScreen(disp)), 1,
		      XYPixmap, 0, data, width, height, 16, bytesperline);

  /* since our data will be in MC68000 format, we force the image structure
   * to agree with it.  neat results if you don't do this.
   */

  image->byte_order= MSBFirst;
  image->bitmap_bit_order= MSBFirst;

  /* set up gc that tells server what colors to use
   */

  gcv.function= GXcopy;
  gcv.foreground= BlackPixel(disp, DefaultScreen(disp));
  gcv.background= WhitePixel(disp, DefaultScreen(disp));
  if (fgcolor && nameToPixel(fgcolor, &gcv.foreground))
    printf(" requested for foreground, using black.\n");
  if (bgcolor && nameToPixel(bgcolor, &gcv.background))
    printf(" requested for background, using white.\n");
  gc= XCreateGC(disp, pic, GCFunction | GCForeground | GCBackground, &gcv);

  /* send image to pixmap.  if we're on a mono screen, we just send it to
   * the destination pixmap.  otherwise we have to send to a mono pixmap
   * and copy that pixmap to the destination pixmap
   */

  if (wa.depth > 1) {
    picplane= XCreatePixmap(disp, RootWindow(disp, DefaultScreen(disp)),
			    pwidth, pheight, 1);
    gcv.function= GXcopy;
    gcv.foreground= 1;
    gcv.background= 0;
    planegc= XCreateGC(disp, picplane, GCFunction | GCForeground |
		       GCBackground, &gcv);

    XPutImage(disp, picplane, planegc, image, cx, cy, xorigin, yorigin,
	      cwidth, cheight);
    XCopyPlane(disp, picplane, pic, gc, xorigin, yorigin,
	       cwidth, cheight, xorigin, yorigin, 1);
    XFreePixmap(disp, picplane);
    XFreeGC(disp, planegc);
  }
  else
    XPutImage(disp, pic, gc, image, cx, cy, xorigin, yorigin,
	      cwidth, cheight);
  XFreeGC(disp, gc);
}

/* send across a color image to a display of the same depth.
 */

void sendColorImage()
{ XImage    *image;
  XGCValues gcv;
  GC        gc;

  image= XCreateImage(disp, DefaultVisual(disp, DefaultScreen(disp)), depth,
		      ZPixmap, 0, data, width, height, 16, bytesperline);
  image->byte_order= MSBFirst;
  image->bitmap_bit_order= MSBFirst;

  gcv.function= GXcopy;
  gc= XCreateGC(disp, pic, GCFunction, &gcv);
  XPutImage(disp, pic, gc, image, cx, cy, xorigin, yorigin, cwidth, cheight);
  XFreeGC(disp, gc);
}

/* send across a color image to whatever
 */

void sendColorPixels()
{ int           x, y;
  unsigned long *dptr;
  XGCValues     gcv;
  GC            gc;

  if (verbose)
    printf("This may take a minute, a pixel at a time is tedious.\n");

  gcv.function= GXcopy;
  gc= XCreateGC(disp, pic, GCFunction, &gcv);

  dptr= pixdata;
  for (y= cy; y < cheight; y++)
    for (x= cx; x < cwidth; x++) {
      XSetForeground(disp, gc, *(dptr++));
      XDrawPoint(disp, pic, gc, x, y);
    }
  XFreeGC(disp, gc);
}
SHAR_EOF
if test -f 'common.h'
then
	echo shar: over-writing existing file "'common.h'"
fi
cat << \SHAR_EOF > 'common.h'
/* common.h:
 *
 * externs for common.c routines and variables
 */

#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

extern Display           *disp;
extern int               width, height, depth;
extern int               bytesperline;
extern int               xorigin, yorigin;
extern int               pwidth, pheight;
extern int               cx, cy;
extern int               cwidth, cheight;
extern int               verbose;
extern int               docorrupt;
extern char              *fgcolor, *bgcolor;
extern Pixmap            pic;
extern XWindowAttributes wa;

int isoption();
int loadImage();
int nameToPixel();
void translateColors();
void sendMonoImage();
void sendColorImage();
void sendColorPixels();
SHAR_EOF
if test -f 'patchlevel.h'
then
	echo shar: over-writing existing file "'patchlevel.h'"
fi
cat << \SHAR_EOF > 'patchlevel.h'
#define PATCHLEVEL 1
SHAR_EOF
if test -f 'rasterfile.h'
then
	echo shar: over-writing existing file "'rasterfile.h'"
fi
cat << \SHAR_EOF > 'rasterfile.h'
/* rasterfile.h:
 *
 * this describes the header for Sun rasterfiles.  if you have SunOS, a
 * better description is in /usr/include/rasterfile.h.  this is used
 * instead to improve portability and to avoid distribution problems.
 */

struct rheader {
  unsigned char magic[4];   /* magic number */
  unsigned char width[4];   /* width of image in pixels */
  unsigned char height[4];  /* height of image in pixels */
  unsigned char depth[4];   /* depth of each pixel */
  unsigned char length[4];  /* length of the image in bytes */
  unsigned char type[4];    /* format of file */
  unsigned char maptype[4]; /* type of colormap */
  unsigned char maplen[4];  /* length of colormap in bytes */
};

/* following the header is the colormap (unless maplen is zero) then
 * the image.  each row of the image is rounded to 2 bytes.
 */

#define RMAGICNUMBER 0x59a66a95 /* magic number of this file type */

/* these are the possible file formats
 */

#define ROLD       0 /* old format, see /usr/include/rasterfile.h */
#define RSTANDARD  1 /* standard format */
#define RRLENCODED 2 /* run length encoding to compress the image */

/* these are the possible colormap types.  if it's in RGB format,
 * the map is made up of three byte arrays (red, green, then blue)
 * that are each 1/3 of the colormap length.
 */

#define RNOMAP  0 /* no colormap follows the header */
#define RRGBMAP 1 /* rgb colormap */
#define RRAWMAP 2 /* raw colormap; good luck */

#define RESC 128 /* run-length encoding escape character */

SHAR_EOF
if test -f 'xbgsun.c'
then
	echo shar: over-writing existing file "'xbgsun.c'"
fi
cat << \SHAR_EOF > 'xbgsun.c'
/* xbgsun.c:
 *
 * Jim Frost, Associative Design Technology
 * 9.21.88
 *
 * this reads a sun raster image file and sets the root window background
 * pixmap to it.
 *
 * this program is in the public domain.
 *
 * 12.30.88 broken apart to use function library common with xviewsun.
 * 12.21.88 bug fix to stop incorrect "bad option" errors.
 * 12.19.88 modified to allow image clipping before loading and to make use
 *          of a default path and/or suffix.  some bug fixes.
 * 12.14.88 modified to load multiple rasterimages onto the same background.
 * 12.08.88 modified to work with color rasterimages, basically a complete
 *          rewrite.
 * 11.27.88 modified to work on color systems and to always deal with
 *          XImage's correctly.
 * 09.21.88 original version
 */

#include "common.h"

void usage(name)
     char *name;
{ printf("Usage: %s [global options] [raster options] rasterfile ...\n",
	 name);
  printf("\nGlobal options:\n");
  printf("    -display display_name   - Specify X display to use\n");
  printf("    -geometry =XxY          - Specify size of destination\n");
  printf("    -verbose | -quiet       - Whistle while you work toggles\n");
  printf("    -border color           - Specify border color\n");
  printf("    -corrupt                - Try to load corrupted image\n");
  printf("\nMore than one rasterfile may be loaded at once, and each may\n");
  printf("be preceeded with its own options from the following list:\n");
  printf("    -at X,Y                 - Load image at coordinates\n");
  printf("    -center                 - Center image\n");
  printf("    -clip X,Y,W,H           - Clip image before loading\n");
  printf("    -foreground color       - Specify foreground for mono image\n");
  printf("    -background color       - Specify background for mono image\n");
  exit(1);
}

main(argc, argv)
int argc;
char *argv[];
{ int       a, x, y,
            tried,     /* a load was attempted */
            loaded,    /* # of images loaded */
            center,    /* center image on pixmap */
            place,     /* image has been placed on pixmap */
            clip,
            setcmap;   /* true if we need to install a colormap */
  char      *dname,    /* display name */
            *bdcolor;  /* border color */
  XGCValues gcv;
  GC        gc;

  if (argc < 2)
    usage(argv[0]);

  tried= loaded= 0;
  dname= bdcolor= NULL;
  pwidth= pheight= 0;
  docorrupt= 0;
  verbose= 1;

  for (a= 1; a < argc; a++)
    if (*argv[a] != '-')
      continue;

    /* parse global options
     */

    else if (isoption("-border", argv[a], 3))
      bdcolor= argv[++a];
    else if (!strcmp("-corrupt", argv[a]))
      docorrupt= 1;
    else if (isoption("-defaults", argv[a], 3)) {
#ifdef PATH
      printf("Default path is '%s'\n", PATH);
#endif
#ifdef SUFFIX
      printf("Default suffix is '%s'\n", SUFFIX);
#else
#ifndef PATH
      printf("There are no defaults\n");
#endif
#endif
      exit(0);
    }
    else if (isoption("-display", argv[a], 3))
      dname= argv[++a];
    else if (isoption("-geometry", argv[a], 2))
      XParseGeometry(argv[++a], &xorigin, &yorigin, &pwidth, &pheight);
    else if (isoption("-help", argv[a], 2))
      usage(argv[0]);
    else if (isoption("-quiet", argv[a], 2))
      verbose= 0;
    else if (isoption("-verbose", argv[a], 2))
      verbose= 1;

    /* strip out local options
     */

    else if (isoption("-center", argv[a], 3))
      ;
    else if (isoption("-at", argv[a], 2) ||
	     isoption("-clip", argv[a], 3) ||
	     isoption("-foreground", argv[a], 2) ||
	     isoption("-background", argv[a], 3))
      if (++a == argc) {
	printf("option '%s' requires an argument\n", argv[a - 1]);
	usage(argv[0]);
      }
      else
	a++;
    else {
      printf("bad argument '%s'\n", argv[a]);
      usage(argv[0]);
    }

  /* open display and get its configuration
   */

  if ((disp= XOpenDisplay(dname)) == NULL) {
    printf("Can't open display.\n");
    exit(1);
  }

  XGetWindowAttributes(disp, RootWindow(disp, DefaultScreen(disp)), &wa);
  if (wa.colormap == 0)
    wa.colormap= DefaultColormap(disp, DefaultScreen(disp));

  /* if we haven't explicitly set the width/height of our background,
   * make it the entire screen
   */

  if (!pwidth && !pheight) {
    pwidth= DisplayWidth(disp, DefaultScreen(disp));
    pheight= DisplayHeight(disp, DefaultScreen(disp));
  }
      
  /* allocate our destination pixmap or window
   */

  pic= XCreatePixmap(disp, RootWindow(disp, DefaultScreen(disp)),
		       pwidth, pheight, wa.depth);

  /* prepare the border area if the any picture was centered
   */

  if (bdcolor) {
    gcv.function= GXcopy;
    gcv.foreground= WhitePixel(disp, DefaultScreen(disp));
    if (nameToPixel(bdcolor, &gcv.foreground))
      printf(" requested for border, using white.\n");
    gc= XCreateGC(disp, pic, GCFunction | GCForeground, &gcv);
    XFillRectangle(disp, pic, gc, 0, 0, pwidth, pheight);
    XFreeGC(disp, gc);
  }

  /* loop through arguments again to do loads
   */

  center= place= clip= 0;
  fgcolor= bgcolor= NULL;
  for (a= 1; a < argc; a++) {


    /* if name doesn't begin with '-', it's a file to load.
     */

    if (*argv[a] != '-') {
      tried= 1;
      if (loadImage(argv[a]) < 0)
	continue;
      loaded++;

      if (center) {
	xorigin= (pwidth - width) / 2;
	yorigin= (pheight - height) / 2;
      }
      else if (!place) {
	xorigin= 0;
	yorigin= 0;
      }

      /* do various adjustments to clip area
       */

      if (clip) {
	if (cx < 0) {
	  cwidth += cx;
	  cx= 0;
	}
	if (cy < 0) {
	  cheight += cy;
	  cy= 0;
	}
	if (cwidth < 1)
	  cwidth= width;
	if (cheight < 1)
	  cheight= height;
	if (cx + cwidth > width)
	  cwidth= width - cx;
	if (cy + cheight > height)
	  cheight= height - cy;
      }
      else {
	cx= cy= 0;
	cwidth= width;
	cheight= height;
      }

      if (depth == 1)
	sendMonoImage();
      else {
	translateColors();
	if (depth == wa.depth)
	  sendColorImage();
	else
	  sendColorPixels();

	if (wa.depth > 1) /* smash in colormap when we're all done */
	  setcmap= 1;
      }

      /* if the first image isn't centered or placed we replicate it
       */

      if ((loaded == 1) && !center && !place) {
	gcv.function= GXcopy;
	gc= XCreateGC(disp, pic, GCFunction, &gcv);
	for (y= 0; y < pheight; y += cheight)
	  for (x= 0; x < pwidth; x += cwidth)
	    XCopyArea(disp, pic, pic, gc, 0, 0, cwidth, cheight, x, y);
	XFreeGC(disp, gc);
      }
      xorigin= yorigin= 0;
      center= place= clip= 0;
      fgcolor= bgcolor= NULL;
    }

    /* not a file, must be an option.
     */

    else if (isoption("-background", argv[a], 3))
      bgcolor= argv[++a];

    else if (isoption("-at", argv[a], 2)) {
      if (center) {
	printf("-center and -at functions conflict, ignoring -at\n");
	a++;
	continue;
      }
      if (place) {
	printf("only one -at to an image, ignoring additional -at\n");
	a++;
	continue;
      }
      if (sscanf(argv[++a], "%d,%d", &xorigin, &yorigin) != 2) {
	printf("bad argument to -at option (ignored)\n");
	continue;
      }
      place= 1;
    }

    else if (isoption("-center", argv[a], 3)) {
      if (place) {
	printf("-at and -center options conflict, ignoring -center\n");
	continue;
      }
      if (center)
	printf("-center already encountered; you're being redundant\n");
      center= 1;
    }

    else if (isoption("-clip", argv[a], 3)) {
      if (clip) {
	printf("cannot clip an image more than once, ignoring -clip\n");
	continue;
      }
      if (sscanf(argv[++a], "%d,%d,%d,%d", &cx, &cy, &cwidth, &cheight) !=
	  4) {
	printf("bad argument to -clip option (ignored)\n");
	continue;
      }
      clip= 1;
    }

    else if (isoption("-border", argv[a], 3) ||  /* skip extra parm if */
	     isoption("-display", argv[a], 3) || /* it's a global with */
	     isoption("-geometry", argv[a], 2))  /* an argument */
      a++;

    else if (isoption("-foreground", argv[a], 2))
      fgcolor= argv[++a];

  }

  if (loaded == 0)
    if (tried)
      exit(1);
    else
      usage(argv[0]);

  /* set up the colormap if necessary and plug in the image.
   */

  if (setcmap) {
    XSetWindowColormap(disp, RootWindow(disp, DefaultScreen(disp)),
		       wa.colormap);
    XInstallColormap(disp, wa.colormap);
  }
  XSetWindowBackgroundPixmap(disp, RootWindow(disp, DefaultScreen(disp)), pic);
  XFreePixmap(disp, pic);
  XClearWindow(disp, RootWindow(disp, DefaultScreen(disp)));
  XSetCloseDownMode(disp, RetainPermanent);
  XCloseDisplay(disp);

  if (verbose && (loaded > 1))
    printf("%d images loaded.\n", loaded);
}
SHAR_EOF
if test -f 'xbgsun.man'
then
	echo shar: over-writing existing file "'xbgsun.man'"
fi
cat << \SHAR_EOF > 'xbgsun.man'
.TH MAN 1 "6 January 1989"
.SH NAME
xbgsun \- load Sun rasterfiles onto an X11 root window
.SH SYNOPSIS
.B xbgsun
[global_options] [raster_options] rasterfile ...
.SH DESCRIPTION
.I Xbgsun
loads one or more Sun rasterfiles onto an X11 root window.  If raster
file names end in .Z, they will be uncompressed before loading (this
saves a lot of disk space).  If configured to use them,
.I xbgsun
will look for files along a default path and/or with a default suffix
in addition to .Z.
.PP
Unless the first image loaded onto the window is centered or
placed, it will be replicated over the entire window, essentially
becoming the background for all subsequent images.
.PP
.I Xbgsun
will load color images onto a display of any depth, although they may
look strange if it cannot allocate the proper colors.  It allocates
only those colors which are used in the image that you are
transferring.  Usually this allows many colormap entries to remain the
same as before you load the image, although it slows down operation
somewhat.  Any combination of image depths may be loaded at one time.
.SH GLOBAL OPTIONS
The following options affect the global operation of
.I xbgsun:
.TP 8
-border color
This sets the background portion of the window which is not covered by
any images to be
.IR color.
.TP
-corrupt
Attempt to load a rasterfile whose image data area has been corrupted.
This is really only useful if the image is shorter than expected.
.TP
-defaults
Displays the default path and suffix which xbgsun will use if it
cannot find the image with the given name.  This options causes
everything else to be ignored.
.TP
-display display_name
X11 display name.
.TP
-geometry =XxY
This sets the size of the window onto which the images are loaded to a
different value than the size of the display.  If the window is
smaller than the display, it will be replicated to fill the display.
.TP
-help
Displays a short summary of xbgsun command line syntax.  This option
causes everything else to be ignored.
.TP
-quiet
Forces
.I xbgsun
to be quiet while it works.  Normally it likes to whistle.
.TP
-verbose
Causes
.I xbgsun
to be talkative, telling you what kind of image it's playing with and
any special processing that it has to do.  This is the default.
.SH RASTER OPTIONS
The following options may preceed each rasterfile.  These options are
local to the rasterfile they preceed.
.TP
-background color
Use
.IR color
as the background color instead of white if you are transferring a
monochrome image to a color display.
.TP
-center
Center the image on the window.
.TP
-at X,Y
Indicates where the image should be loaded onto the window.
.TP
-clip X,Y,W,H
Clip the image before loading it.  X and Y are the coordinates to
start clipping at, W is the width to use, and H is the height.  If W
or H are zero or negative, clipping will start at (X,Y) and will
continue to the end of the image.  Clipping boundaries are adjusted to
fit within the image (eg -10,0,100,100 for a 50x50 image will be
adjusted to 0,0,50,50).  This is useful when you would only like a
portion of an image, such as the girl (guy) in the middle of an image
but not all the guys (girls) around her (him).
.TP
-foreground color
Use
.IR color
as the foreground color instead of black if you are transferring a
monochrome image to a color display.
.SH EXAMPLES
To load the rasterfile "raster.sun" onto the background and replicate
it to fill the entire background:
.sp
.ti +5
xbgsun raster.sun
.PP
To load a monochrome image "raster.sun" onto the background, using red
as the foreground color, replicate the image, and overlay
"raster2.sun" onto it at coordinate (10,10):
.sp
.ti +5
xbgsun -foreground red raster.sun -at 10,10 raster2.sun
.PP
To center the rectangular region from 10 to 110 along the X axis and
from 10 to the height of the image along the Y axis:
.sp
.ti +5
xbgsun -center -clip 10,10,100,0 raster.sun
.SH AUTHOR
.nf
Jim Frost
Associative Design Technology
madd@bu-it.bu.edu
.SH BUGS
Xbgsun assumes that the number of colors in a colormap is power(2,
depth).  This might be bogus on some systems.
.PP
Things can look strange if you transfer an image of depth
.I n
to a display with a smaller depth (eg color to monochrome).
SHAR_EOF
if test -f 'xviewsun.c'
then
	echo shar: over-writing existing file "'xviewsun.c'"
fi
cat << \SHAR_EOF > 'xviewsun.c'
/* xbgsun.c:
 *
 * Jim Frost, Associative Design Technology
 * 12.15.88
 *
 * this reads a sun raster image file and displays it in a window.  it is
 * derived from the 12.14.88 version of xbgsun.
 *
 * 12.30.80 broken apart to use a function library common with xbgsun.
 * 12.15.88 original version
 */

#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>

extern Display           *disp;
extern int               width, height, depth;
extern int               bytesperline;
extern int               xorigin, yorigin;
extern int               pwidth, pheight;
extern int               cx, cy;
extern int               cwidth, cheight;
extern int               verbose;
extern int               docorrupt;
extern char              *fgcolor, *bgcolor;
extern Pixmap            pic;
extern XWindowAttributes wa;

void usage(name)
     char *name;
{ printf("Usage: %s [-display display_name] [-geometry X_geometry] \
[-foreground color] [-background color] [-corrupt] rasterfile\n", name);
  exit(1);
}

void nuked(disp)
Display *disp;
{
  exit(0);
}

/* this returns the name of the file with no added junk
 */

char *tail(name)
char *name;
{ int a;

  if (strlen(name) < 2)
    return(name);
  for (a= strlen(name); (a > 0) && (name[a - 1] != '/'); a--)
    ;
  return(&name[a]);
}

main(argc, argv)
int argc;
char *argv[];
{ int                  a, dummy,
                       x, y;      /* where to put the window */
  char                 *dname;    /* display name */
  XGCValues            gcv;
  GC                   gc;
  Window               window;    /* window for image */
  XSetWindowAttributes swa;
  XSizeHints           sh;
  union {
    XAnyEvent    any;
    XExposeEvent expose;
  } xevent;

  if (argc < 2)
    usage(argv[0]);

  dname= NULL;
  verbose= 1;
  docorrupt= 0;
  xorigin= yorigin= cx= cy= 0;

  /* parse options
   */

  for (a= 1; (a < argc) && (*argv[a] == '-'); a++)
    if (isoption("-help", argv[a], 2))
      usage(argv[0]);
    else if (!strcmp("-corrupt", argv[a]))
      docorrupt= 1;
    else if (isoption("-display", argv[a], 2))
      dname= argv[++a];
    else if (isoption("-geometry", argv[a], 2))
      XParseGeometry(argv[++a], &x, &y, &dummy, &dummy);
    else if (isoption("-foreground", argv[a], 2))
      fgcolor= argv[++a];
    else if (isoption("-background", argv[a], 2))
      bgcolor= argv[++a];
  if (a != argc - 1)
    usage(argv[0]);

  /* try to load the image
   */

  if (loadImage(argv[a]) < 0)
    exit(1);
  cwidth= pwidth= width;
  cheight= pheight= height;

  /* open display and get its configuration
   */

  if ((disp= XOpenDisplay(dname)) == NULL) {
    printf("Can't open display.\n");
    exit(1);
  }
  XSetIOErrorHandler(nuked);

  XGetWindowAttributes(disp, RootWindow(disp, DefaultScreen(disp)), &wa);
  if (wa.colormap == 0)
    wa.colormap= DefaultColormap(disp, DefaultScreen(disp));

  /* allocate our destination pixmap or window
   */

  pic= XCreatePixmap(disp, RootWindow(disp, DefaultScreen(disp)),
		       width, height, wa.depth);

  /* get the image to the server
   */

  if (depth == 1)
    sendMonoImage();
  else {
    translateColors();
    if (depth == wa.depth)
      sendColorImage();
    else
      sendColorPixels();
  }

  /* get a window for the image
   */

  swa.event_mask= ButtonPressMask | ExposureMask | EnterWindowMask |
    LeaveWindowMask;
  window= XCreateWindow(disp, RootWindow(disp, DefaultScreen(disp)),
			x, y, width, height, 0, wa.depth, InputOutput,
			CopyFromParent, CWEventMask, &swa);
  XStoreName(disp, window, tail(argv[a]));
  XSetIconName(disp, window, tail(argv[a]));
  sh.width= width;
  sh.height= height;
  sh.flags= USSize;
  XSetNormalHints(disp, window, &sh);

  gcv.function= GXcopy;
  gc= XCreateGC(disp, window, GCFunction, &gcv);

  XMapWindow(disp, window);

  /* set up the colormap if necessary and set up the window
   */

  if (wa.depth > 1)
    XSetWindowColormap(disp, RootWindow(disp, DefaultScreen(disp)),
		       wa.colormap);
  XCopyArea(disp, pic, window, gc, 0, 0, width, height, 0, 0);

  for (;;) {
    XNextEvent(disp, &xevent);
    switch(xevent.any.type) {
    case ButtonPress :
      XFreePixmap(disp, pic);
      XDestroyWindow(disp, window);
      XCloseDisplay(disp);
      exit(0);

    case Expose :
      XCopyArea(disp, pic, window, gc, xevent.expose.x, xevent.expose.y,
		xevent.expose.width, xevent.expose.height,
		xevent.expose.x, xevent.expose.y);
      break;

    case EnterNotify :
      if (wa.depth > 1)
	XInstallColormap(disp, wa.colormap);
      break;

    case LeaveNotify :
      if (wa.depth > 1)
	XUninstallColormap(disp, wa.colormap);
      break;
    }
  }
}
SHAR_EOF
if test -f 'xviewsun.man'
then
	echo shar: over-writing existing file "'xviewsun.man'"
fi
cat << \SHAR_EOF > 'xviewsun.man'
.TH MAN 1 "6 January 1988"
.SH NAME
xviewsun \- look at a Sun rasterfile in an X11 window
.SH SYNOPSIS
.B xviewsun
[ -display
.IR display_name
]
[ -geometry
.IR X_geometry
]
[ -foreground
.IR color
]
[ -background
.IR color
]
.IR rasterfile
.SH DESCRIPTION
.I Xviewsun
loads a Sun rasterfile into an X11 window.  If the raster file name
ends in .Z, it will be uncompressed before loading (this saves a lot
of disk space).
.PP
.I Xviewsun
will show a color image on a display of any depth, although it may
look strange if it cannot allocate the proper colors.  It allocates
only those colors which are used in the image that you are
transferring.  Usually this allows many colormap entries to remain the
same as before you load the image, although it slows down operation
somewhat.
.SH OPTIONS
The following options are available:
.TP 8
-display display_name
X11 display name.
.TP
-geometry X_geometry
This sets the location of the window.  Size information is ignored
since the size is set by the image to be displayed.
.TP
-foreground color
Use
.IR color
as the foreground color instead of black if you are transferring a
monochrome image to a color display.
.TP
-background color
Use
.IR color
as the background color instead of white if you are transferring a
monochrome image to a color display.
.TP
-corrupt
Force the load of an image file which has been truncated in the image
data portion.
.SH AUTHOR
.nf
Jim Frost
Associative Design Technology
madd@bu-it.bu.edu
.SH BUGS
Xviewsun assumes that the number of colors in a colormap is power(2,
depth).  This might be bogus on some systems.
.PP
Things can look strange if you transfer an image of depth
.I n
to a display with a smaller depth (eg color to monochrome).
.PP
The name
.IR xviewsun
is close enough to SunView (which is probably a trademark of Sun
Microsystems) to be confusing.  Some might call this a feature.
SHAR_EOF
#	End of shell archive
exit 0