[comp.sys.sgi] GIF file viewer

phillips@grads.cs.ubc.ca (George Phillips) (09/02/89)

Since I got more requests for this program than I could count on my
fingers, I decided to post it.  Included is a GIF file viewer and
a viewer for 24 bit images as found on venera.isi.edu.  These programs both
require a 24 bit deep display and have only been tried on a personal iris.
I hope they'll end up in the archives at vgr.brl.mil, but if not I'll
try and put them there myself.  I look forward to your improvements.


George Phillips phillips@cs.ubc.ca {alberta,uw-beaver,uunet}!ubc-cs!phillips


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile igif.c decoder.c errs.h std.h vimg.c
# Wrapped by phillips@grads.cs.ubc.ca on Fri Sep  1 21:34:12 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(793 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThere are 2 programs here.  igif will display GIF files on a personal
Xiris workstation.  vimg displays 24 bit RGB files in the venera imglib
Xformat.  Images in the imglib format can be found on venera.isi.edu.
X
XEach program uses very simple interfaces to graphics and windows.  They
Xwork and are more or less directly transliterated from examples in the
Xgraphics documentation that came with our iris.
X
XBoth programs could use improvement, like getting rid of 24 bit mode or
Xadding scroll bars and so on.  If you do make improvements, please send
Xthem to me (phillips@cs.ubc.ca {alberta,uw-beaver,uunet}!ubc-cs!phillips)
Xand maybe I can keep track of future versions.
X
XBut most of all, have fun displaying images.
X
XGeorge Phillips
XDepartment of Computer Science
XUniversity of British Columbia
END_OF_FILE
if test 793 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(273 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XCFLAGS=
X
X# If this doesn't work, try LIB=-Zg
XLIB=-lgl_s
X
Xall: igif vimg
X
Xigif: igif.o decoder.o
X	cc -o igif igif.o decoder.o $(LIB)
X
Xdecoder.o: errs.h std.h
X
Xvimg: vimg.o
X	cc -o vimg vimg.o $(LIB)
X
Xshar:
X	shar README Makefile igif.c decoder.c errs.h std.h vimg.c >img.shar
END_OF_FILE
if test 273 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'igif.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'igif.c'\"
else
echo shar: Extracting \"'igif.c'\" \(6871 characters\)
sed "s/^X//" >'igif.c' <<'END_OF_FILE'
X/*
X * igif.c -- display a GIF image on the personal iris.
X *
X * usage:	igif [ file.gif ]
X *
X * Will display the GIF file given as the first argument or a GIF file
X * read from standard input if no arguments are given.
X *
X * This is not a particularly pretty piece of code.  This works on the
X * personal iris we have here, but has not been tested on other irises
X * or other SGI machines.  It uses 24 bit RGB mode because that's all
X * I have looked into (plus it's easy :-).  GIF images are pretty much
X * limited to 256 colours, so using some sort of colourmap mode should
X * be possible.  Those with more experience could also modify the program
X * to display multiple GIF images with a single invocation.
X *
X * Copyright 1989 by George Phillips
X *
X * Permission is granted to freely distribute this program in whole or in
X * part provided you don't make money off it, you don't pretend that you
X * wrote it and that this notice accompanies the code.
X *
X * The GIF LZW decoder was written by someone else who's copyright notice
X * is contained in decode.c.
X *
X * George Phillips <phillips@grads.cs.ubc.ca>
X * Department of Computer Science
X * University of British Columbia
X */
X
X#include <stdio.h>
X#include <malloc.h>
X#include <gl.h>
X#include <device.h>
X
X#include "errs.h"
X
Xint bad_code_count;
XFILE* glob_fp;
Xstatic char* gif_name;
X
Xmain(argc, argv)
Xint		argc;
Xchar*	argv[];
X{
X	FILE*			fp;
X
X	if (argc > 2) {
X		fprintf(stderr, "Sorry, I can only do one GIF file at a time.\n");
X		exit(1);
X	}
X
X	if (argc == 1) {
X		gif_name = "standard input";
X		gif_display(glob_fp = stdin);
X	}
X	else {
X		char* p;
X		char* lastslash;
X
X		for (p = argv[1], lastslash = argv[1] - 1; *p; p++)
X			if (*p == '/')
X				lastslash = p;
X
X		gif_name = lastslash + 1;
X
X		if ((fp = fopen(argv[1], "r")) == NULL) {
X			fprintf(stderr, "can't open '%s'\n", argv[1]);
X			exit(1);
X		}
X		gif_display(glob_fp = fp);
X	}
X	screen_manage();
X	exit(0);
X}
X
Xstatic int err = 0;
Xstatic long global[256];
Xstatic long local[256];
Xstatic long* cmap;
Xstatic long* screen;
Xstatic long* lineptr;
Xstatic int	i_y;
Xstatic int	pass;
Xstatic long* imagestart;
Xstatic int s_width;
Xstatic int s_height;
Xstatic int i_height;
Xstatic int is_interlaced;
X
X#define error(x)	printf("%s at byte %d\n", x, ftell(fp)); return(0)
X#define reterr(x)	if (err != 0) { error(x); }
X#define reteof()	reterr("Unexpected EOF")
X#define skipbyte(x)	getbyte(x); reteof()
X
Xgif_display(fp)
XFILE*	fp;
X{
X	static unsigned char buf[4096];
X	int s_control, back;
X	int i_top, i_left, i_width, i_control;
X	int ch;
X	int nc;
X	int	i, j;
X
X	/* read signature */
X	if (fread(buf, 3, 1, fp) != 1) {
X		error("File too short");
X	}
X	
X	if (strncmp(buf, "GIF", 3)) {
X		error("Not a GIF file");
X	}
X
X	if (fread(buf, 3, 1, fp) != 1) {
X		error("File too short");
X	}
X
X	if (strncmp(buf, "87a", 3)) {
X		buf[3] = '\0';
X		printf("unknown version '%s'\n", buf);
X		return(0);
X	}
X	
X	/* read screen descriptor */
X	s_width = getword(fp); reteof();
X	s_height = getword(fp); reteof();
X	s_control = getbyte(fp); reteof();
X	back = getbyte(fp); reteof();
X	skipbyte(fp);
X
X	if ((screen = (long*)malloc(sizeof(long) * s_width * s_height)) == NULL) {
X		fprintf(stderr, "not enough memory for screen, bye!\n");
X		exit(1);
X	}
X	/* sure, I should use bzero. Whatever */
X	{ register int i;
X		for (i = 0; i < s_width * s_height; i++)
X			screen[i] = 0;
X	}
X	prefsize(s_width, s_height);
X	winopen(gif_name);
X	RGBmode();
X	gconfig();
X
X	if (s_control & 128) { /* global colour map */
X		if (fread(buf, (2 << (s_control & 7)) * 3, 1, fp) != 1) {
X			error("EOF in global colourmap");
X		}
X		gif2rgb(buf, global, 2 << (s_control & 7));
X	}
X
X	for (;;) {
X		ch = getbyte(fp);
X		reterr("no terminator");
X		switch (ch) {
X		case ',':	/* image follows */
X			i_left = getword(fp);	reteof();
X			i_top = getword(fp);	reteof();
X			i_width = getword(fp); reteof();
X			i_height = getword(fp); reteof();
X			i_control = getbyte(fp); reteof();
X			is_interlaced = i_control & 64; 
X			nc = 2 << (s_control & 7);
X			cmap = global;
X			if (i_control & 128) { /* local colour map */
X				if (fread(buf, (2 << (i_control & 7)) * 3, 1, fp) != 1) {
X					error("EOF in local colourmap");
X				}
X				nc = 2 << (i_control & 7);
X				gif2rgb(buf, local, nc);
X				cmap = local;
X			}
X			bad_code_count = 0;
X			imagestart = lineptr = screen + (s_width * i_top) + i_left +
X					(i_height - 1) * s_width;
X			i_y = 0;
X			pass = 0;
X			decoder(i_width);
X			/* ignore rest of blocks used by decoder */
X			skipblocks(fp); reterr("Bad block in image");
X			break;
X		case ';':	/* terminator ... */
X			return(0);
X		case '!':	/* extension block */
X			skipbyte(fp);	/* function code */
X			skipblocks(fp); reterr("Bad block in extension block");
X			break;
X		default:
X			/* Supposed to ignore any unknown characters up to the image
X			 * separator, but I prefer to be tight about these things because
X			 * there are many corrupt images out there.
X			 */
X			printf("Unknown block type '%c' (%d) at byte %d\n",
X				ch, ch, ftell(fp));
X			return(0);
X		}
X	}
X}
X
X/* Convert the GIF colourmap into long rgb components which can be directly
X * plunked into an lrect.
X *
X * GIF colourmap is a byte stream: rgbrgbrgb
X * lrect format is in 4 byte words: abgr abgr abgr
X * (the a is alpha, which should be zero)
X */
Xgif2rgb(g, r, len)
Xchar*	g;
Xlong*	r;
Xint		len;
X{
X	register int i;
X	register int j;
X
X	for (i = j = 0; i < len; i++, j += 3)
X		r[i] = g[j] | (g[j+1] << 8) | (g[j+2] << 16);
X}
X
Xstatic int pass_width[] = { 8, 8, 4, 2 };
Xstatic int pass_start[] = { 0, 4, 2, 1 };
X
Xout_line(pixel, linelen)
Xunsigned char* pixel;
Xint	linelen;
X{
X	register int i;
X
X	for (i = 0; i < linelen; i++)
X		lineptr[i] = cmap[pixel[i]];
X	
X	if (!is_interlaced) {
X		lineptr -= s_width;
X	}
X	else {
X		lineptr -= pass_width[pass] * s_width;
X		i_y += pass_width[pass];
X		if (i_y >= i_height) {
X			pass++;
X			i_y = pass_start[pass];
X			lineptr = imagestart - pass_start[pass] * s_width;
X		}
X	}
X}
X
Xint get_byte()
X{
X	return(getbyte(glob_fp));
X}
X
Xint getbyte(fp)
XFILE*	fp;
X{
X	int	ch;
X
X	err = 0;
X	if ((ch = fgetc(fp)) == EOF) {
X		err = 1;
X		return(READ_ERROR);
X	}
X	return(ch & 255);
X}
X
Xint getword(fp)
XFILE*	fp;
X{
X	int	c1, c2;
X
X	err = 0;
X	if ((c1 = fgetc(fp)) == EOF) {
X		err = 1;
X		return(0);
X	}
X	if ((c2 = fgetc(fp)) == EOF) {
X		err = 1;
X		return(0);
X	}
X	return(((c2 & 255) << 8) | (c1 & 255));
X}
X
Xskipblocks(fp)
XFILE*	fp;
X{
X	int len;
X	static char buf[256];
X
X	err = 0;
X
X	for (;;) {
X		len = getbyte(fp);
X		reterr("EOF in blocks");
X
X		if (len == 0)
X			return(0);
X
X		if (fread(buf, len, 1, fp) != 1) {
X			puts("EOF in blocks");
X			err = 1;
X			return(0);
X		}
X	}
X}
X
Xscreen_manage()
X{
X	lrectwrite(0, 0, s_width - 1, s_height - 1, screen);
X
X	qdevice(REDRAW);
X	qdevice(PIECECHANGE);
X	qdevice(WINQUIT);
X
X	while (1) {
X		short data;
X
X		switch (qread(&data)) {
X		case REDRAW:
X		case PIECECHANGE:
X			lrectwrite(0, 0, s_width - 1, s_height - 1, screen);
X			break;
X		case WINQUIT:
X			exit(0);
X		default:
X			break;
X		}
X	}
X}
X
END_OF_FILE
if test 6871 -ne `wc -c <'igif.c'`; then
    echo shar: \"'igif.c'\" unpacked with wrong size!
fi
# end of 'igif.c'
fi
if test -f 'decoder.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'decoder.c'\"
else
echo shar: Extracting \"'decoder.c'\" \(12570 characters\)
sed "s/^X//" >'decoder.c' <<'END_OF_FILE'
X/* DECODE.C - An LZW decoder for GIF
X * Copyright (C) 1987, by Steven A. Bennett
X *
X * Permission is given by the author to freely redistribute and include
X * this code in any program as long as this credit is given where due.
X *
X * In accordance with the above, I want to credit Steve Wilhite who wrote
X * the code which this is heavily inspired by...
X *
X * GIF and 'Graphics Interchange Format' are trademarks (tm) of
X * Compuserve, Incorporated, an H&R Block Company.
X *
X * Release Notes: This file contains a decoder routine for GIF images
X * which is similar, structurally, to the original routine by Steve Wilhite.
X * It is, however, somewhat noticably faster in most cases.
X *
X * GWP -- if GINFO if defined, it will use a global variable and
X * stdio routines in place of most of the get_byte calls to speed
X * things along.
X */
X
X#define GINFO
X
X#ifdef GINFO
X
X#include <stdio.h>
Xextern FILE* glob_fp;
X
X#endif
X
X#include "std.h"
X#include "errs.h"
X
XIMPORT TEXT *malloc();                 /* Standard C library allocation */
X
X/* IMPORT INT get_byte()
X *
X *   - This external (machine specific) function is expected to return
X * either the next byte from the GIF file, or a negative number, as
X * defined in ERRS.H.
X */
XIMPORT INT get_byte();
X
X/* IMPORT INT out_line(pixels, linelen)
X *     UBYTE pixels[];
X *     INT linelen;
X *
X *   - This function takes a full line of pixels (one byte per pixel) and
X * displays them (or does whatever your program wants with them...).  It
X * should return zero, or negative if an error or some other event occurs
X * which would require aborting the decode process...  Note that the length
X * passed will almost always be equal to the line length passed to the
X * decoder function, with the sole exception occurring when an ending code
X * occurs in an odd place in the GIF file...  In any case, linelen will be
X * equal to the number of pixels passed...
X */
XIMPORT INT out_line();
X
X/* IMPORT INT bad_code_count;
X *
X * This value is the only other global required by the using program, and
X * is incremented each time an out of range code is read by the decoder.
X * When this value is non-zero after a decode, your GIF file is probably
X * corrupt in some way...
X */
XIMPORT INT bad_code_count;
X
X#ifndef GINFO
X#define NULL   0L
X#endif
X
X#define MAX_CODES   4095
X
X/* Static variables */
XLOCAL WORD curr_size;                     /* The current code size */
XLOCAL WORD clear;                         /* Value for a clear code */
XLOCAL WORD ending;                        /* Value for a ending code */
XLOCAL WORD newcodes;                      /* First available code */
XLOCAL WORD top_slot;                      /* Highest code for current size */
XLOCAL WORD slot;                          /* Last read code */
X
X/* The following static variables are used
X * for seperating out codes
X */
XLOCAL WORD navail_bytes = 0;              /* # bytes left in block */
XLOCAL WORD nbits_left = 0;                /* # bits left in current byte */
XLOCAL UTINY b1;                           /* Current byte */
XLOCAL UTINY byte_buff[257];               /* Current block */
XLOCAL UTINY *pbytes;                      /* Pointer to next byte in block */
X
XLOCAL LONG code_mask[13] = {
X     0,
X     0x0001, 0x0003,
X     0x0007, 0x000F,
X     0x001F, 0x003F,
X     0x007F, 0x00FF,
X     0x01FF, 0x03FF,
X     0x07FF, 0x0FFF
X     };
X
X
X/* This function initializes the decoder for reading a new image.
X */
XLOCAL WORD init_exp(size)
X   WORD size;
X   {
X   curr_size = size + 1;
X   top_slot = 1 << curr_size;
X   clear = 1 << size;
X   ending = clear + 1;
X   slot = newcodes = ending + 1;
X   navail_bytes = nbits_left = 0;
X   return(0);
X   }
X
X/* get_next_code()
X * - gets the next code from the GIF file.  Returns the code, or else
X * a negative number in case of file errors...
X */
XLOCAL WORD get_next_code()
X   {
X   WORD i, x;
X   ULONG ret;
X
X   if (nbits_left == 0)
X      {
X      if (navail_bytes <= 0)
X         {
X
X         /* Out of bytes in current block, so read next block
X          */
X         pbytes = byte_buff;
X#ifdef GINFO
X		if ((navail_bytes = fgetc(glob_fp)) == EOF)
X			return(READ_ERROR);
X#else
X         if ((navail_bytes = get_byte()) < 0)
X            return(navail_bytes);
X#endif
X         else if (navail_bytes)
X            {
X#ifdef GINFO
X			if (fread(byte_buff, navail_bytes, 1, glob_fp) != 1)
X				return(READ_ERROR);
X#else
X            for (i = 0; i < navail_bytes; ++i)
X               {
X               if ((x = get_byte()) < 0)
X                  return(x);
X               byte_buff[i] = x;
X               }
X#endif
X            }
X         }
X      b1 = *pbytes++;
X      nbits_left = 8;
X      --navail_bytes;
X      }
X
X   ret = b1 >> (8 - nbits_left);
X   while (curr_size > nbits_left)
X      {
X      if (navail_bytes <= 0)
X         {
X
X         /* Out of bytes in current block, so read next block
X          */
X         pbytes = byte_buff;
X#ifdef GINFO
X		 if ((navail_bytes = fgetc(glob_fp)) == EOF)
X			return(READ_ERROR);
X#else
X         if ((navail_bytes = get_byte()) < 0)
X            return(navail_bytes);
X#endif
X         else if (navail_bytes)
X            {
X#ifdef GINFO
X			if (fread(byte_buff, navail_bytes, 1, glob_fp) != 1)
X				return(READ_ERROR);
X#else
X            for (i = 0; i < navail_bytes; ++i)
X               {
X               if ((x = get_byte()) < 0)
X                  return(x);
X               byte_buff[i] = x;
X               }
X#endif
X            }
X         }
X      b1 = *pbytes++;
X      ret |= b1 << nbits_left;
X      nbits_left += 8;
X      --navail_bytes;
X      }
X   nbits_left -= curr_size;
X   ret &= code_mask[curr_size];
X   return((WORD)(ret));
X   }
X
X
X/* The reason we have these seperated like this instead of using
X * a structure like the original Wilhite code did, is because this
X * stuff generally produces significantly faster code when compiled...
X * This code is full of similar speedups...  (For a good book on writing
X * C for speed or for space optomisation, see Efficient C by Tom Plum,
X * published by Plum-Hall Associates...)
X */
XLOCAL UTINY stack[MAX_CODES + 1];            /* Stack for storing pixels */
XLOCAL UTINY suffix[MAX_CODES + 1];           /* Suffix table */
XLOCAL UWORD prefix[MAX_CODES + 1];           /* Prefix linked list */
X
X/* WORD decoder(linewidth)
X *    WORD linewidth;               * Pixels per line of image *
X *
X * - This function decodes an LZW image, according to the method used
X * in the GIF spec.  Every *linewidth* "characters" (ie. pixels) decoded
X * will generate a call to out_line(), which is a user specific function
X * to display a line of pixels.  The function gets it's codes from
X * get_next_code() which is responsible for reading blocks of data and
X * seperating them into the proper size codes.  Finally, get_byte() is
X * the global routine to read the next byte from the GIF file.
X *
X * It is generally a good idea to have linewidth correspond to the actual
X * width of a line (as specified in the Image header) to make your own
X * code a bit simpler, but it isn't absolutely necessary.
X *
X * Returns: 0 if successful, else negative.  (See ERRS.H)
X *
X */
X
XWORD decoder(linewidth)
X   WORD linewidth;
X   {
X   FAST UTINY *sp, *bufptr;
X   UTINY *buf;
X   FAST WORD code, fc, oc, bufcnt;
X   WORD c, size, ret;
X
X   /* Initialize for decoding a new image...
X    */
X   if ((size = get_byte()) < 0)
X      return(size);
X   if (size < 2 || 9 < size)
X      return(BAD_CODE_SIZE);
X   init_exp(size);
X
X   /* Initialize in case they forgot to put in a clear code.
X    * (This shouldn't happen, but we'll try and decode it anyway...)
X    */
X   oc = fc = 0;
X
X   /* Allocate space for the decode buffer
X    */
X   if ((buf = (UTINY *)malloc(linewidth + 1)) == NULL)
X      return(OUT_OF_MEMORY);
X
X   /* Set up the stack pointer and decode buffer pointer
X    */
X   sp = stack;
X   bufptr = buf;
X   bufcnt = linewidth;
X
X   /* This is the main loop.  For each code we get we pass through the
X    * linked list of prefix codes, pushing the corresponding "character" for
X    * each code onto the stack.  When the list reaches a single "character"
X    * we push that on the stack too, and then start unstacking each
X    * character for output in the correct order.  Special handling is
X    * included for the clear code, and the whole thing ends when we get
X    * an ending code.
X    */
X   while ((c = get_next_code()) != ending)
X      {
X
X      /* If we had a file error, return without completing the decode
X       */
X      if (c < 0)
X         {
X         free(buf);
X         return(0);
X         }
X
X      /* If the code is a clear code, reinitialize all necessary items.
X       */
X      if (c == clear)
X         {
X         curr_size = size + 1;
X         slot = newcodes;
X         top_slot = 1 << curr_size;
X
X         /* Continue reading codes until we get a non-clear code
X          * (Another unlikely, but possible case...)
X          */
X         while ((c = get_next_code()) == clear)
X            ;
X
X         /* If we get an ending code immediately after a clear code
X          * (Yet another unlikely case), then break out of the loop.
X          */
X         if (c == ending)
X            break;
X
X         /* Finally, if the code is beyond the range of already set codes,
X          * (This one had better NOT happen...  I have no idea what will
X          * result from this, but I doubt it will look good...) then set it
X          * to color zero.
X          */
X         if (c >= slot)
X            c = 0;
X
X         oc = fc = c;
X
X         /* And let us not forget to put the char into the buffer... And
X          * if, on the off chance, we were exactly one pixel from the end
X          * of the line, we have to send the buffer to the out_line()
X          * routine...
X          */
X         *bufptr++ = c;
X         if (--bufcnt == 0)
X            {
X            if ((ret = out_line(buf, linewidth)) < 0)
X               {
X               free(buf);
X               return(ret);
X               }
X            bufptr = buf;
X            bufcnt = linewidth;
X            }
X         }
X      else
X         {
X
X         /* In this case, it's not a clear code or an ending code, so
X          * it must be a code code...  So we can now decode the code into
X          * a stack of character codes. (Clear as mud, right?)
X          */
X         code = c;
X
X         /* Here we go again with one of those off chances...  If, on the
X          * off chance, the code we got is beyond the range of those already
X          * set up (Another thing which had better NOT happen...) we trick
X          * the decoder into thinking it actually got the last code read.
X          * (Hmmn... I'm not sure why this works...  But it does...)
X          */
X         if (code >= slot)
X            {
X            if (code > slot)
X               ++bad_code_count;
X            code = oc;
X            *sp++ = fc;
X            }
X
X         /* Here we scan back along the linked list of prefixes, pushing
X          * helpless characters (ie. suffixes) onto the stack as we do so.
X          */
X         while (code >= newcodes)
X            {
X            *sp++ = suffix[code];
X            code = prefix[code];
X            }
X
X         /* Push the last character on the stack, and set up the new
X          * prefix and suffix, and if the required slot number is greater
X          * than that allowed by the current bit size, increase the bit
X          * size.  (NOTE - If we are all full, we *don't* save the new
X          * suffix and prefix...  I'm not certain if this is correct...
X          * it might be more proper to overwrite the last code...
X          */
X         *sp++ = code;
X         if (slot < top_slot)
X            {
X            suffix[slot] = fc = code;
X            prefix[slot++] = oc;
X            oc = c;
X            }
X         if (slot >= top_slot)
X            if (curr_size < 12)
X               {
X               top_slot <<= 1;
X               ++curr_size;
X               } 
X
X         /* Now that we've pushed the decoded string (in reverse order)
X          * onto the stack, lets pop it off and put it into our decode
X          * buffer...  And when the decode buffer is full, write another
X          * line...
X          */
X         while (sp > stack)
X            {
X            *bufptr++ = *(--sp);
X            if (--bufcnt == 0)
X               {
X               if ((ret = out_line(buf, linewidth)) < 0)
X                  {
X                  free(buf);
X                  return(ret);
X                  }
X               bufptr = buf;
X               bufcnt = linewidth;
X               }
X            }
X         }
X      }
X   ret = 0;
X   if (bufcnt != linewidth)
X      ret = out_line(buf, (linewidth - bufcnt));
X   free(buf);
X   return(ret);
X   }
X
END_OF_FILE
if test 12570 -ne `wc -c <'decoder.c'`; then
    echo shar: \"'decoder.c'\" unpacked with wrong size!
fi
# end of 'decoder.c'
fi
if test -f 'errs.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'errs.h'\"
else
echo shar: Extracting \"'errs.h'\" \(366 characters\)
sed "s/^X//" >'errs.h' <<'END_OF_FILE'
X/* Various error codes used by decoder
X * and my own routines...   It's okay
X * for you to define whatever you want,
X * as long as it's negative...  It will be
X * returned intact up the various subroutine
X * levels...
X */
X#define OUT_OF_MEMORY -10
X#define BAD_CODE_SIZE -20
X#define READ_ERROR -1
X#define WRITE_ERROR -2
X#define OPEN_ERROR -3
X#define CREATE_ERROR -4
X
END_OF_FILE
if test 366 -ne `wc -c <'errs.h'`; then
    echo shar: \"'errs.h'\" unpacked with wrong size!
fi
# end of 'errs.h'
fi
if test -f 'std.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'std.h'\"
else
echo shar: Extracting \"'std.h'\" \(278 characters\)
sed "s/^X//" >'std.h' <<'END_OF_FILE'
X/* STD.H - My own standard header file...
X */
X
X#define LOCAL static
X#define IMPORT extern
X
X#define FAST register
X
Xtypedef short WORD;
Xtypedef unsigned short UWORD;
Xtypedef char TEXT;
Xtypedef unsigned char UTINY;
Xtypedef long LONG;
Xtypedef unsigned long ULONG;
Xtypedef int INT;
X
END_OF_FILE
if test 278 -ne `wc -c <'std.h'`; then
    echo shar: \"'std.h'\" unpacked with wrong size!
fi
# end of 'std.h'
fi
if test -f 'vimg.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'vimg.c'\"
else
echo shar: Extracting \"'vimg.c'\" \(4677 characters\)
sed "s/^X//" >'vimg.c' <<'END_OF_FILE'
X/*
X * vimg -- display a venera imglib 24 bit image.
X *
X * usage: vimg picture
X *	or
X *		  vimg picture.a
X *
X * This program will display the 24 bit RGB images which can be ftp'ed from
X * venera.isi.edu.  The format is very simple.  Using some base name (like
X * "picture"), the following files are relevant:
X *
X *	picture.a		-- contains width and height information
X *	picture.r		-- red component of image (1 byte per pixel)
X *	picture.g		-- green component of image (ditto)
X *	picture.b		-- blue component of image (ditto)
X *
X * The program will either take a base name or the .a version.  Also, if any of
X * the files are compressed, it will run uncompress -c on them.  In other words,
X * it will also look for .r.Z, .b.Z and .g.Z files.  I'm not really sure what
X * the standard iris setup is, but if you don't have /usr/bsd in you path,
X * it won't work.  Either add it to your path or change "uncompress" to 
X * /usr/bsd/uncompress in the program.
X *
X * BUGS:
X *
X * If image is bigger than 1024 on any side, then I should clip it.  But I
X * don't since it gives me the urge for scrollbars...
X *
X * It seems if any optimization is turned on, then it will break if any of the
X * red, green or blue components (.r, .g or .b files) is _not_ compressed.
X *
X *
X * Copyright 1989 by George Phillips
X *
X * Permission is granted to freely distribute this program in whole or in
X * part provided you don't make money off it, you don't pretend that you
X * wrote it and that this notice accompanies the code.
X *
X * George Phillips <phillips@grads.cs.ubc.ca>
X * Department of Computer Science
X * University of British Columbia
X */
X
X#include <gl.h>
X#include <stdio.h>
X#include <malloc.h>
X#include <device.h>
X
Xint width;
Xint height;
X
Xlong* img;
Xchar* buf;
X
Xmain(argc, argv)
Xint argc;
Xchar* argv[];
X{
X	int i;
X	FILE* fp;
X	char att[256], red[256], green[256], blue[256];
X	char* myalloc();
X	FILE* myopen();
X	char* p;
X	char* lastdot;
X	char* shortname;
X	char ch;
X	char dimen[256];
X
X	if (argc != 2) {
X		fprintf(stderr, "usage: vimg basename OR vimg basename.a\n");
X		exit(1);
X	}
X
X	shortname = argv[1] - 1;
X	lastdot = NULL;
X	for (p = argv[1]; *p; p++)
X		if (*p == '.')
X			lastdot = p;
X		else if (*p == '/')
X			shortname = p;
X	
X	shortname++;
X	
X	if (lastdot != NULL && !strcmp(lastdot, ".a"))
X		*lastdot = '\0';
X
X	sprintf(att, "%s.a", argv[1]);
X	fp = myopen(att);
X	fgets(dimen, 256, fp);
X	ch = dimen[4];
X	dimen[4] = '\0';
X	width = atoi(dimen);
X	dimen[4] = ch;
X	dimen[8] = '\0';
X	height = atoi(dimen + 4);
X
X	printf("%s: width = %d, height = %d\n", argv[1], width, height);
X	myclose();
X
X	img = (long*)myalloc(sizeof(long) * width * height);
X	buf = myalloc(width);
X
X	for (i = 0; i < width * height; i++)
X		img[i] = 0;
X
X	sprintf(red, "%s.r", argv[1]);
X	printf("reading %s\n", red);
X	getcom(myopen(red), 0);
X	myclose();
X
X	sprintf(green, "%s.g", argv[1]);
X	printf("reading %s\n", green);
X	getcom(myopen(green), 8);
X	myclose();
X
X	sprintf(blue, "%s.b", argv[1]);
X	printf("reading %s\n", blue);
X	getcom(myopen(blue), 16);
X	myclose();
X
X	prefsize(width, height);
X	winopen(shortname);
X	RGBmode();
X	gconfig();
X	lrectwrite(0, 0, width - 1, height - 1, img);
X
X	qdevice(REDRAW);
X	qdevice(PIECECHANGE);
X	qdevice(WINQUIT);
X
X	while (1) {
X		short data;
X
X		switch (qread(&data)) {
X		case REDRAW:
X		case PIECECHANGE:
X			lrectwrite(0, 0, width - 1, height - 1, img);
X			break;
X		case WINQUIT:
X			exit(0);
X		default:
X			break;
X		}
X	}
X}
X
Xgetcom(fp, shift)
XFILE* fp;
Xint shift;
X{
X	FILE* fp;
X	long* line;
X	int i;
X
X	line = img + width * height - width;
X	while (fread(buf, width, 1, fp) == 1) {
X		if (line < img) {
X			printf("extra data, oh well, ignored\n");
X			break;
X		}
X		for (i = 0; i < width; i++)
X			line[i] |= (buf[i] << shift);
X		line -= width;
X	}
X}
X
Xfatal(s)
Xchar* s;
X{
X	fprintf(stderr, "%s\n", s);
X	exit(1);
X}
X
X/* open a file, or a pipe to uncompress -c if it is compressed */
X/* myclose is very hardwired, but will work as long as only 1 file */
X/* is opened by myopen at a time */
X
Xint ftype;
XFILE* glob_fp;
X
XFILE* myopen(fname)
Xchar* fname;
X{
X	FILE* fp;
X	char cmd[256];
X
X	if ((fp = fopen(fname, "r")) == NULL) {
X		sprintf(cmd, "%s.Z", fname);
X		if (access(cmd, 4) < 0) {
X			fprintf(stderr, "Can't open %s or %s.Z\n", fname, fname);
X			exit(1);
X		}
X		sprintf(cmd, "uncompress -c %s.Z", fname);
X		if ((fp = popen(cmd, "r")) == NULL) {
X			fprintf(stderr, "can't open %s or %s.Z\n", fname, fname);
X			exit(1);
X		}
X		ftype = 2;
X	}
X	else
X		ftype = 1;
X	return(glob_fp = fp);
X}
X
Xmyclose()
X{
X	if (ftype == 1)
X		fclose(glob_fp);
X	else
X		pclose(glob_fp);
X}
X
X/* malloc or die! */
Xchar* myalloc(size)
Xint size;
X{
X	char* p;
X
X	if ((p = malloc(size)) == NULL) {
X		fprintf(stderr, "out of memory\n");
X		exit(1);
X	}
X	return(p);
X}
END_OF_FILE
if test 4677 -ne `wc -c <'vimg.c'`; then
    echo shar: \"'vimg.c'\" unpacked with wrong size!
fi
# end of 'vimg.c'
fi
echo shar: End of shell archive.
exit 0

bobf@BLUMIRIS.CHEM.UMR.EDU ("Robert B. Funchess") (05/05/90)

As stated before, one is available for anonymous FTP from here.  The address,however, has been changed: clciris.chem.umr.edu (131.151.14.48).
--
Bob Funchess					bobf@blumiris.chem.umr.edu
Chemistry Dept.					University of Missouri - Rolla