[alt.sources] igif 2.0 part 1/2

phillips@cs.ubc.ca (George Phillips) (09/07/90)

Here is the latest version of igif, a program for displaying GIF images
on SGI machines.  The README file below outlines some of the new
features.  I've testing it on an 8 plane personal Iris and on a
24 plane machine.  It should run on anything with 24 planes and on
any 8 plane machine running 4Sight (NeWS).  A little hacking could
make it run on machines in-between.

Igif has some mininal GIF version 89a support, but some day I plan
to extend that and maybe even throw in support for other image
file formats.  So have fun and send in any bug reports, etc.

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

#! /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:
#	README
#	igif.1
#	Makefile
#	igif.c
# This archive created: Thu Sep  6 22:51:22 1990
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1375 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XHere is igif, a program for displaying GIF images on SGI machines.
X
XThe new version has the following features:
X	- displays all images in a GIF file and multiple GIF files at once
X	- uses much less memory
X	- incremental refresh and displays images while loading
X	- can be made to stay in the foreground
X	- displays as much of the image as possible (for corrupt files)
X	- window label changes to indicate the GIF file displayed
X	- works on 8 bit displays and machines without lrectwrite automatically
X	- menu for selecting from multiple GIF files
X	- incremental reading of images for fast response
X
XIgif is also available via anonymous ftp from cs.ubc.ca: /src/igif2.shar.Z.
X
X
XCopyright 1989,1990 by George Phillips
X
XPermission to use, copy, modify, and distribute this software and its
Xdocumentation for any purpose and without fee is hereby granted, provided
Xthat the above copyright notice appear in all copies and that both that
Xcopyright notice and this permission notice appear in supporting
Xdocumentation.  This software is provided "as is" without express or
Ximplied warranty.
X
XThe GIF LZW decoder was written by someone else who's copyright notice
Xis contained in decode.c.
X
XThe Floyd-Steinberg dithering code was based on ppmquant from Jef
XPoskanzer's PBM+ package.
X
XGeorge Phillips <phillips@cs.ubc.ca>
XDepartment of Computer Science
XUniversity of British Columbia
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'igif.1'" '(2648 characters)'
if test -f 'igif.1'
then
	echo shar: will not over-write existing file "'igif.1'"
else
sed 's/^X//' << \SHAR_EOF > 'igif.1'
X.TH IGIF 1 "5 September, 1990"
X
X.if n .ds Q \&"
X.if n .ds U \&"
X.if t .ds Q ``
X.if t .ds U ''
X
X.SH NAME
Xigif \- display GIF images on SGI machines
X
X.SH SYNOPSIS
X.B igif
X[ \-d \-e \-f \-l \-t \-2 ] { file.gif }
X
X.SH DESCRIPTION
X.I igif
Xreads the GIF files given and displays them in a window.  If no files are
Xgiven,
X.I igif
Xreads a GIF image from standard input.  Some options control how the GIF
Ximages are displayed.
X
X.TP
X.B \-d
XDither the image before displaying it.  This option has no effect on
Xmachines with 24 bit displays.
X
X.TP
X.B \-e
XErase the window entirely before moving to a new image.
X
X.TP
X.B \-f
XStay in the foreground.  This is useful for scripts which want to
Xwait for
X.I igif
Xto finish before they continue.
X
X.TP
X.B \-l
XDo not use lrectwrite.
X.I igif
Xshould detect when lrectwrite is not availble, but this option will give
Xyou a workaround if it guesses wrong.  This option only applies to machines
Xwith 24 bit displays.
X
X.TP
X.B \-t
XDisplay the image in true colour.  This may not always be possible and it
Xmay have adverse affects on other applications running.  This option has
Xno effect on machines with 24 bit displays.
X
X.TP
X.B \-2
XDisplay the images twice as wide and high.  This is nice for those itsy-bitsy
X320 x 200 GIF images.
X
X.SH "WINDOW INTERFACE"
XIf you are displaying more than one image,
X.I igif
Xgives you three ways to select which images to display.  Pressing the left
Xmouse button or the \*Qn\*U key will move forward to the next image.  Pressing
Xthe middle mouse button or the \*Qp\*U key will move backward to the
Xprevious image.  The right mouse button will display a menu of images to
Xselect from.  Since
X.I igif
Xtries to bring images up as quickly as possible, you may select an image which
Xhas not been loaded.  In this case nothing will happen.
XWhen the numbers no longer appear on the left of the
Xwindow title bar, all the images will be loaded.
X
XPressing the \*Qq\*U key will quit the program.
X
X.SH BUGS
XIf some program has munged the \*Qstandard\*U 8 plane colourmap, the images
Xwill not look too good.  Either run makemap (/usr/sbin/makemap, perhaps)
Xor use the -t flag.  
X.I igif
Xmay itself wipe out the colourmap if you kill it.
X
X.PP
XAuto-dection of a missing lrectwrite(\|) may not work on all systems.
XI'll be happy to apply a fix if you have one.
X
X.PP
XInterlaced GIF files are dithered as they are loaded instead of waiting
Xuntil we have all the lines in the right order.  This is wrong.
X
X.PP
XDither should be done after enlarging the image.  It isn't so using \-d
Xand \-2 may give a poor result.
X
X.SH AUTHOR
XGeorge Phillips
X.br
XDepartment of Computer Science
X.br
XUniversity of British Columbia
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'Makefile'" '(1093 characters)'
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
XCFLAGS=-g
X
X# If this doesn't work, try LIB=-Zg
XLIB=-lgl_s
X
Xigif: igif.o decoder.o newsmap.o floydstein.o
X	cc $(CFLAGS) -o igif igif.o decoder.o newsmap.o floydstein.o $(LIB)
X
X#igif: igif.u decoder.u newsmap.u floydstein.u
X#	cc $(CFLAGS) -o igif igif.u decoder.u newsmap.u floydstein.u $(LIB)
X	
Xclean:
X	rm -f igif a.out core Makefile.old igif2.shar igif2.1.shar igif2.2.shar
X	rm -f *.o
X
Xshar:
X	shar -pX -v README igif.1 Makefile igif.c decoder.c newsmap.c floydstein.c \
X		errs.h std.h mem_image.h newsmap.h imgfile.h > igif2.shar
Xshar2:
X	shar -pX -v README igif.1 Makefile igif.c > igif2.1.shar
X	shar -pX -v decoder.c newsmap.c floydstein.c \
X		errs.h std.h mem_image.h newsmap.h imgfile.h > igif2.2.shar
X
X# If you put something after here, makedep will destroy it!
Xdecoder.o: errs.h mem_image.h std.h
Xerrs.h:
X	touch errs.h
Xfloydstein.o: mem_image.h newsmap.h
Xigif.o: errs.h imgfile.h mem_image.h newsmap.h
Ximgfile.h:
X	touch imgfile.h
Xmem_image.h:
X	touch mem_image.h
Xnewsmap.o: newsmap.h
Xnewsmap.h:
X	touch newsmap.h
Xstd.h:
X	touch std.h
X.PRECIOUS:  errs.h imgfile.h mem_image.h newsmap.h std.h
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'igif.c'" '(27914 characters)'
if test -f 'igif.c'
then
	echo shar: will not over-write existing file "'igif.c'"
else
sed 's/^X//' << \SHAR_EOF > 'igif.c'
X/*
X * igif.c -- display GIF images on the personal iris and other SGI machines.
X *
X * usage:	igif [ -d -e -f -l -t -2 ] { file.gif }
X *
X * If no images are specified, a GIF image is read from standard input.
X * flags:
X *
X *	-d	Dither the images before displaying it.
X *	-e	Erase the entire previous image before going to the next one.
X *	-f	Stay in the foreground; this is useful for 3 line browsers:
X *			foreach f (*.gif)
X *				igif -f $f
X *			end
X *	-l	Force igif to not use lrectwrite.  This may be faster and it
X *		may be necessary for it to work ('though some autodection is done).
X *	-t	Display true colours on colour-mapped displays.  On displays with
X *		only 8 planes, igif will change the colours of images to fit the
X *		standard NeWS colourmap.  This option makes igif display the true
X *		colours; even if that means that other programs will get their
X *		colours (temporarily) stomped on.  It tries hard to be as
X *		inconspicuous as possible, but if you gotta have 256 colours...
X *	-2	Display the images in double height and double width mode.  A
X *		cheap hack, but it saves some eyestrain on those itsy-bitsy
X *		320 x 200 GIFs.
X *
X * When in igif, the left and middle mouse buttons (or the n and p keys)
X * move to the next and previous images respectively.  The right mouse
X * button brings up a menu of the images.  The q key will quit igif.
X * Until all the images are loaded, you may not see the image even if you
X * move to it.  When no numbers appear to the left of the title bar all
X * images should be loaded.
X *
X * Bugs:
X *	If some program has munged the "standard" 8 plane colourmap, the images
X *	won't look too good.  Either run makemap (/usr/sbin/makemap, perhaps)
X *	or use the -t flag.  Igif may itself wipe out the colourmap if you
X *	kill it.
X *	Auto-dection of a missing lrectwrite() may not work on all systems.
X *	I'll be happy to apply a fix if you have one.
X *	Dithering should be done after doubling the image.  It isn't and the
X *	results are not too pretty.
X *
X * TODO list:
X *	- we can't dither interlaced GIFs right away (but we do anyways!)
X *	- support GIF89a fully
X *	- work on displays without RGB but > 8 planes.  Not too hard,
X *		but _I_ can't test it.
X *	- true colour mode should check if a local colourmap is in fact the same.
X *	- set NeWS colourmap on startup?
X *	- display meaninful information if an image is not yet loaded.
X *	- transmogrification into a generalized image viewer which supports
X *		lots of different formats, perhaps in concert with PBM+.
X *	- generalize the magnification (esp. to correct aspect ratio)
X *	- options to perform gamma correction of images
X *	- clean up the code
X *
X * Copyright 1989,1990 by George Phillips
X *
X * Permission to use, copy, modify, and distribute this software and its
X * documentation for any purpose and without fee is hereby granted, provided
X * that the above copyright notice appear in all copies and that both that
X * copyright notice and this permission notice appear in supporting
X * documentation.  This software is provided "as is" without express or
X * implied warranty.
X *
X * The GIF LZW decoder was written by someone else who's copyright notice
X * is contained in decode.c.
X *
X * The Floyd-Steinburg dithering code was based on ppmquant from Jef
X * Poskanzer's PBM+ package.
X *
X * George Phillips <phillips@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#include "mem_image.h"
X#include "imgfile.h"
X#include "newsmap.h"
X
Xint bad_code_count;
X
Xstruct imgfile*	imf_list = NULL;
Xstruct imgfile**	imf_ins = &imf_list;
Xstruct mem_image*	cur_img = NULL;
X
Xint	stay_in_foreground = 0;
Xint mag = 1;
Xint use_lrectwrite = 1;
Xint true_colours = 0;
Xint dither = 0;
Xint	erase_all = 0;
Xint timing = 0;
X
Xenum disp_mode_type { RGB_LRECT, RGB_WRITE, CMAP_NEWS, CMAP_TRUE };
Xstatic enum disp_mode_type disp_mode;
Xstatic int win_width;
Xstatic int win_height;
Xint redrawing = 0;
Xint redraw_line = 0;
Xstruct imgfile* cur_imf = NULL;
Xlong imgmenu;
X
Xint loading;
X
Xchar* image_title();
X
Xmain(argc, argv)
Xint		argc;
Xchar*	argv[];
X{
X	FILE*	fp;
X	int		argn;
X	int		i;
X	int		maxwidth;
X	int		maxheight;
X	struct imgfile*	imf;
X
X	argn = 1;
X	for (argn = 1; argn < argc && argv[argn][0] == '-'; argn++) {
X		if (!strcmp(argv[argn], "-f"))
X			stay_in_foreground = 1;
X		else if (!strcmp(argv[argn], "-l"))
X			use_lrectwrite = 0;
X		else if (!strcmp(argv[argn], "-t"))
X			true_colours = 1;
X		else if (!strcmp(argv[argn], "-d"))
X			dither = 1;
X		else if (!strcmp(argv[argn], "-e"))
X			erase_all = 1;
X		else if (!strcmp(argv[argn], "-2")) {
X			mag = 2;
X		}
X		else if (!strcmp(argv[argn], "-T"))
X			timing = 1;
X		else {
X			fprintf(stderr, "igif: unknown option '%s'\n", argv[argn]);
X			exit(1);
X		}
X	}
X
X	if (true_colours && dither) {
X		fprintf(stderr, "igif: only one of -t and -d may be specified; -t assumed\n");
X		dither = 0;
X	}
X
X	if (argn == argc)
X		setup_file("standard input", stdin);
X	else
X		for (i = argn; i < argc; i++)
X			setup_file(argv[i], (FILE*)NULL);
X	
X	loading = 0;
X	maxwidth = 10;
X	maxheight = 10;
X	for (imf = imf_list; imf != NULL; imf = imf->next) {
X		loading++;
X		if (imf->width > maxwidth)
X			maxwidth = imf->width;
X		if (imf->height > maxheight)
X			maxheight = imf->height;
X	}
X	
X	if (loading == 0) {
X		fprintf(stderr, "igif: no images to display\n");
X		exit(1);
X	}
X
X	screen_init(maxwidth, maxheight);
X	if (dither && (disp_mode == RGB_LRECT || disp_mode == RGB_WRITE))
X		dither = 0;
X
X	for (imf = imf_list; imf != NULL; imf = imf->next) {
X		if (imf->stream != NULL || (fp = fopen(imf->filename, "r")) != NULL) {
X			if (cur_imf == NULL) {
X				cur_imf = imf;
X				wintitle(image_title(imf));
X			}
X			if (imf->stream != NULL)
X				load_gif(imf, imf->stream, imf->width, imf->height);
X			else {
X				load_gif(imf, fp, -1, -1);
X				fclose(fp);
X			}
X			loading--;
X			update_loadinfo();
X		}
X	}
X
X	update_loadinfo();
X	screen_handle(1);
X	exit(0);
X}
X
Xsizeof_gif(name, fp, width, height)
Xchar*	name;
XFILE*	fp;
Xint*	width;
Xint*	height;
X{
X	unsigned char buf[10];
X
X	if (fread(buf, 10, 1, fp) != 1) {
X		fprintf(stderr, "igif: %s is too short\n", name);
X		return(0);
X	}
X	if (strncmp(buf, "GIF", 3)) {
X		fprintf(stderr, "igif: %s is not a GIF file\n", name);
X		return(0);
X	}
X	if (strncmp(buf + 3, "87a", 3) && strncmp(buf + 3, "89a", 3)) {
X		buf[6] = '\0';
X		fprintf(stderr, "igif: %s is an unsupported GIF file version\n", name);
X		return(0);
X	}
X	*width = buf[6] + (buf[7] << 8);
X	*height = buf[8] + (buf[9] << 8);
X	return(1);
X}
X
Xstatic int err = 0;
Xstatic int	i_y;
Xstatic int	pass;
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
Xint* gif2rgb();
Xstruct mem_image* add_image();
Xvoid adjust_colourmap();
X
Xsetup_file(filename, stream)
Xchar*	filename;
XFILE*	stream;
X{
X	FILE*	fp;
X	int		width, height;
X	struct imgfile* imf;
X	char* p;
X	char* lastslash;
X
X	if (stream != NULL)
X		fp = stream;
X	else if ((fp = fopen(filename, "r")) == NULL) {
X		fprintf(stderr, "can't open '%s'\n", filename);
X		return;
X	}
X
X	if (sizeof_gif(filename, fp, &width, &height)) {
X		imf = (struct imgfile*)malloc(sizeof(struct imgfile));
X		if (imf == NULL)
X			no_mem();
X		imf->filename = filename;
X		imf->stream = stream;
X		imf->width = width;
X		imf->height = height;
X
X		for (p = filename, lastslash = filename - 1; *p; p++)
X			if (*p == '/')
X				lastslash = p;
X		
X		imf->name = lastslash + 1;
X		imf->imglist = NULL;
X		imf->next = NULL;
X		*imf_ins = imf;
X		imf_ins = &(imf->next);
X	}
X	if (stream == NULL)
X		fclose(fp);
X}
X
Xload_gif(imf, fp, s_width, s_height)
Xstruct imgfile*	imf;
XFILE*	fp;
Xint		s_width;
Xint		s_height;
X{
X	static unsigned char buf[4096];
X	int s_control, back;
X	int i_top, i_left, i_width, i_control, i_height;
X	int ch;
X	int	i, j;
X	int*	global_colourmap;
X	int*	local_colourmap;
X	struct mem_image* img;
X	struct mem_image** img_ins;
X
X	img_ins = &(imf->imglist);
X
X	if (s_width == -1) {
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) && strncmp(buf, "89a", 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	}
X
X	s_control = getbyte(fp); reteof();
X	back = getbyte(fp); reteof();
X	skipbyte(fp);
X
X	if (s_control & 128) { /* global colour map */
X		global_colourmap = gif2rgb(fp, 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
X			if (i_control & 128) { /* local colour map */
X				local_colourmap = gif2rgb(fp, 2 << (i_control & 7));
X				img = add_image(i_width, i_height, 2 << (i_control & 7),
X					s_width, s_height);
X				img->colourmap = local_colourmap;
X				img->maplen = 2 << (i_control & 7);
X			}
X			else {
X				img = add_image(i_width, i_height, 2 << (s_control & 7),
X					s_width, s_height);
X				img->colourmap = global_colourmap;
X				img->maplen = 2 << (s_control & 7);
X			}
X			img->x_off += i_left * mag;
X			img->y_off += i_top * mag;
X			img->gif_interlaced = i_control & 64; 
X			img->background = back;
X			img->imf = imf;
X			*img_ins = img;
X			img_ins = &(img->next);
X			adjust_colourmap(img);
X			if (img->imf == cur_imf && img->imf->imglist == img)
X				paint_background(img);
X			bad_code_count = 0;
X			i_y = 0;
X			pass = 0;
X			decoder(fp, img);
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/*
X * Read the GIF colourmap from the given file and convert it into
X * the format used by lrectwrite.
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 */
Xint* gif2rgb(fp, len)
XFILE*	fp;
Xint		len;
X{
X	register int i;
X	int*	rgb;
X
X	if ((rgb = (int*)malloc(len * sizeof(int))) == NULL)
X		no_mem();
X
X	/* very loose about EOF, oh well */
X	for (i = 0; i < len; i++) {
X		rgb[i] = fgetc(fp);
X		rgb[i] |= fgetc(fp) << 8;
X		rgb[i] |= fgetc(fp) << 16;
X	}
X	return(rgb);
X}
X
Xstatic int pass_width[] = { 8, 8, 4, 2 };
Xstatic int pass_start[] = { 0, 4, 2, 1 };
X
Xchar* out_line(img)
Xstruct mem_image*	img;
X{
X	if (dither)
X		floydstein(img, i_y);
X
X	if (disp_mode == CMAP_TRUE)
X		true_cmap(img, i_y);
X
X	if (cur_imf == img->imf) {
X		switch (disp_mode) {
X		case CMAP_TRUE:
X		case CMAP_NEWS:
X			cmap_incr_redraw(img, i_y);
X			break;
X		case RGB_LRECT:
X			incr_redraw(img, i_y);
X			break;
X		case RGB_WRITE:
X			nonrect_incr_redraw(img, i_y);
X			break;
X		}
X	}
X	screen_handle(0);
X
X	if (!img->gif_interlaced)
X		return(img->data + ++i_y * img->width);
X
X	i_y += pass_width[pass];
X	if (i_y >= img->height) {
X		pass++;
X		i_y = pass_start[pass];
X	}
X	return(img->data + i_y * img->width);
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
Xvoid set_display_mode();
Xvoid save_colourmap();
Xvoid set_colourmap();
Xvoid toggle_colourmap();
Xvoid restore_colourmap();
X
Xscreen_init(maxwidth, maxheight)
Xint	maxwidth;
Xint	maxheight;
X{
X	struct imgfile*	imf;
X
X	init_newsmap();
X
X	if (stay_in_foreground)
X		foreground();
X
X	prefsize(win_width = maxwidth * mag, win_height = maxheight * mag);
X	if (winopen("igif") < 0) {
X		fprintf(stderr, "igif: couldn't open a window.\n");
X		exit(1);
X	}
X	set_display_mode();
X
X	if (disp_mode == RGB_LRECT || disp_mode == RGB_WRITE)
X		RGBmode();
X
X	gconfig();
X
X	if (disp_mode == RGB_LRECT)
X		rectzoom((float)mag, (float)mag);
X
X	if (disp_mode == CMAP_TRUE)
X		save_colourmap();
X
X	rectf(0, 0, win_width - 1, win_height - 1);
X
X	qdevice(REDRAW);
X	qdevice(PIECECHANGE);
X	qdevice(WINQUIT);
X	qdevice(LEFTMOUSE);
X	qdevice(MIDDLEMOUSE);
X	qdevice(INPUTCHANGE);
X
X	qdevice(NKEY);
X	qdevice(PKEY);
X	qdevice(QKEY);
X
X	qdevice(MENUBUTTON);
X	imgmenu = newpup();
X	addtopup(imgmenu, "images %t");
X	for (imf = imf_list; imf != NULL; imf = imf->next)
X		addtopup(imgmenu, imf->name);
X}
X
Xscreen_handle(block)
Xint	block;
X{
X	short	data;
X	int		i;
X	struct imgfile*	imf;
X
X	if (block)
X		update_loadinfo();
X
X	do {
X		while (qtest()) {
X			switch (qread(&data)) {
X			case MENUBUTTON:
X				if ((i = dopup(imgmenu)) <= 0)
X					break;
X				for (cur_imf = imf_list; i > 1; i--)
X					cur_imf = cur_imf->next;
X				new_imf();
X				break;
X			case PKEY:
X			case MIDDLEMOUSE:
X				if (data == 0)
X					break;
X				for (imf = imf_list; imf != NULL; imf = imf->next)
X					if (imf->next == cur_imf ||
X					(imf->next == NULL && cur_imf == imf_list))
X						break;
X
X				cur_imf = imf;
X				new_imf();
X				break;
X			case NKEY:
X			case LEFTMOUSE:
X				/* no need to redraw if we have only one image */
X				if (imf_list->next == NULL || data == 0)
X					break;
X				if ((cur_imf = cur_imf->next) == NULL)
X					cur_imf = imf_list;
X				new_imf();
X				break;
X			case REDRAW:
X			case PIECECHANGE:
X				if (cur_imf != NULL) {
X					cur_img = cur_imf->imglist;
X					if (cur_img != NULL) {
X						redrawing = 1;
X						redraw_line = 0;
X						paint_background(cur_img);
X					}
X					else
X						redrawing = 0;
X				}
X				break;
X			case QKEY:
X			case WINQUIT:
X				if (disp_mode == CMAP_TRUE)
X					restore_colourmap();
X				exit(0);
X			case INPUTCHANGE:
X				if (disp_mode == CMAP_TRUE && cur_imf->imglist != NULL)
X					toggle_colourmap(cur_imf->imglist);
X				break;
X			default:
X				break;
X			}
X		}
X		if (redrawing) {
X			switch (disp_mode) {
X			case CMAP_TRUE:
X			case CMAP_NEWS:
X				redraw_line = cmap_incr_redraw(cur_img, redraw_line);
X				break;
X			case RGB_LRECT:
X				redraw_line = incr_redraw(cur_img, redraw_line);
X				break;
X			case RGB_WRITE:
X				redraw_line = nonrect_incr_redraw(cur_img, redraw_line);
X				break;
X			}
X
X			if (redraw_line >= cur_img->height) {
X				cur_img = cur_img->next;
X				if (cur_img == NULL)
X					redrawing = 0;
X				else
X					redraw_line = 0;
X			}
X		}
X	} while (block);
X}
X
Xnew_imf()
X{
X	wintitle(image_title(cur_imf));
X	cur_img = cur_imf->imglist;
X	if (cur_img != NULL) {
X		if (disp_mode == CMAP_TRUE)
X			set_colourmap(cur_img);
X		paint_background(cur_img);
X		redrawing = 1;
X		redraw_line = 0;
X	}
X	else
X		redrawing = 0;
X}
X
Xupdate_loadinfo()
X{
X	if (cur_imf != NULL)
X		wintitle(image_title(cur_imf));
X}
X
Xno_mem()
X{
X	fprintf(stderr, "out of memory\n");
X	exit(1);
X}
X
X
Xstruct mem_image* add_image(width, height, depth, scr_width, scr_height)
Xint		width;
Xint		height;
Xint		depth;
Xint		scr_width;
Xint		scr_height;
X{
X	struct mem_image*	img;
X
X	if ((img = (struct mem_image*)malloc(sizeof(struct mem_image))) == NULL)
X		no_mem();
X	
X	img->width = width;
X	img->height = height;
X	img->depth = depth;
X	img->colourmap = NULL;
X	img->mapmap = NULL;
X	img->gif_interlaced = 0;
X	img->seq = -1;
X
X	img->x_off = (win_width - scr_width * mag) / 2;
X	img->y_off = (win_height - scr_height * mag) / 2;
X	/*img->state = LOADING;*/
X
X	if ((img->data = malloc(width * height)) == NULL)
X		no_mem();
X
X	bzero(img->data, width * height);
X
X	img->next = NULL;
X	
X	return(img);
X}
X
Xchar* image_title(imf)
Xstruct imgfile*	imf;
X{
X	static char title[256];
X	char	lbuf[32];
X
X	sprintf(lbuf, "%d ", loading);
X
X	sprintf(title, "%s%s",
X		loading > 0 ? lbuf : "",
X		imf->name);
X
X	return(title);
X}
X
X
Xpaint_background(img)
Xstruct mem_image* img;
X{
X	int	xleft, xright, ytop, ybottom;
X
X	switch (disp_mode) {
X	case RGB_LRECT:
X		RGBcolor(img->colourmap[img->background] & 255,
X				 (img->colourmap[img->background] >> 8) & 255,
X				 (img->colourmap[img->background] >> 16) & 255);
X		break;
X	case RGB_WRITE:
X		RGBcolor(*((char*)img->colourmap + img->background),
X				 *((char*)img->colourmap + img->maplen + img->background),
X				 *((char*)img->colourmap + img->maplen * 2 + img->background));
X		break;
X	case CMAP_NEWS:
X		color(rgb2newsmap(img->colourmap[img->background] & 255,
X						  (img->colourmap[img->background] >> 8) & 255,
X						  (img->colourmap[img->background] >> 16) & 255));
X		break;
X	case CMAP_TRUE:
X		if (img->mapmap[img->background] >= 0)
X			color(img->mapmap[img->background]);
X		else {
X			int	m;
X
X			/* we haven't mapped any colours yet so collision isn't possible */
X			m = rgb2newsmap(img->colourmap[img->background] & 255,
X							(img->colourmap[img->background] >> 8) & 255,
X							(img->colourmap[img->background] >> 16) & 255);
X			img->mapmap[img->background] = m;
X			color(m);
X		}
X	}
X
X	if (erase_all) {
X		rectf(0, 0, win_width, win_height);
X		return;
X	}
X
X	if (img->imf->width >= win_width && img->imf->height >= win_height)
X		return;
X
X	xleft = (win_width - img->imf->width * mag) / 2;
X	xright = win_width - img->imf->width * mag - xleft;
X	ybottom = (win_height - img->imf->height * mag) / 2;
X	ytop = win_height - img->imf->height * mag - ybottom;
X
X	rectf(0, 0, win_width - 1, ybottom);
X	rectf(0, 0, xleft, win_height - 1);
X	rectf(0, win_height - ytop, win_width - 1, win_height - 1);
X	rectf(win_width - xright, 0, win_width - 1, win_height - 1);
X}
X
Xint incr_redraw(img, line)
Xregister struct mem_image*	img;
Xregister int				line;
X{
X	static int argb_buf[XMAXSCREEN];
X	register char* p;
X	register int* argb;
X
X	p = img->data + line * img->width + img->width - 1;
X	argb = argb_buf + img->width - 1;
X
X	while (argb >= argb_buf)
X		*argb-- = img->colourmap[*p--];
X
X	lrectwrite(img->x_off,
X			(win_height - (line + 1) * mag) - img->y_off,
X			img->width /* * mag */ - 1 + img->x_off,
X			(win_height - (line + 1) * mag) - img->y_off,
X			argb_buf);
X
X	return(line + 1);
X}
X
X
X/* This is based on code From: moss@BRL.MIL ("Gary S. Moss", VLD/VMB) */
X
Xint nonrect_incr_redraw(img, line)
Xregister struct mem_image*	img;
Xregister int				line;
X{
X	static unsigned char Red_pixels[XMAXSCREEN];
X	static unsigned char Green_pixels[XMAXSCREEN];
X	static unsigned char Blue_pixels[XMAXSCREEN];
X	register short i;
X	register char* p;
X	register char* red_map;
X	register char* green_map;
X	register char* blue_map;
X
X	p = img->data + line * img->width;
X
X	red_map = (char*)img->colourmap;
X	green_map = (char*)img->colourmap + img->maplen;
X	blue_map = (char*)img->colourmap + img->maplen * 2;
X
X	for (i = 0; i < img->width * mag; i++, p++)  {
X		Red_pixels[i] = red_map[*p];
X		Green_pixels[i] = green_map[*p];
X		Blue_pixels[i] = blue_map[*p];
X		if (mag == 2) {
X			Red_pixels[++i] = red_map[*p];
X			Green_pixels[i] = green_map[*p];
X			Blue_pixels[i] = blue_map[*p];
X		}
X	}
X	
X	cmov2i(img->x_off, (win_height - (line + 1) * mag) - img->y_off);
X	writeRGB(img->width * mag, Red_pixels, Green_pixels, Blue_pixels);
X	if (mag == 2) {
X		cmov2i(img->x_off, (win_height - (line + 1) * mag) - img->y_off + 1);
X		writeRGB(img->width * mag, Red_pixels, Green_pixels, Blue_pixels);
X	}
X
X	return(line + 1);
X}
X
Xint cmap_incr_redraw(img, line)
Xregister struct mem_image*	img;
Xregister int				line;
X{
X	static unsigned short cmap[XMAXSCREEN];
X	register short i;
X	register char* p;
X
X	p = img->data + line * img->width;
X
X	for (i = 0; i < img->width * mag; i++, p++)  {
X		cmap[i] = img->mapmap[*p];
X		if (mag == 2)
X			cmap[++i] = img->mapmap[*p];
X	}
X	
X	cmov2i(img->x_off, (win_height - (line + 1) * mag) - img->y_off);
X	writepixels(img->width * mag, cmap);
X	if (mag == 2) {
X		cmov2i(img->x_off, (win_height - (line + 1) * mag) - img->y_off + 1);
X		writepixels(img->width * mag, cmap);
X	}
X
X	return(line + 1);
X}
X
X/* auto-dectection code From:    <cditi!caw@uunet.uucp> */
X
Xvoid set_display_mode()
X{
X	char buf[32];
X
X	gversion(buf);
X
X	if (getplanes() <= 8)
X		disp_mode = true_colours ? CMAP_TRUE : CMAP_NEWS;
X	else if (strncmp(buf, "GL4D-", 5) || !use_lrectwrite)
X		disp_mode = RGB_WRITE;
X	else
X		disp_mode = RGB_LRECT;
X}
X
Xstruct map_entry {
X	int	orig_index;
X	int	abgr;
X	int	screen_index;
X	int	used;
X};
X
Xstatic int colour_used[256];
X
Xint map_ent_cmp(a, b)
Xstruct map_entry*	a;
Xstruct map_entry*	b;
X{
X	if (!a->used)
X		return(1);
X	if (!b->used)
X		return(-1);
X	return(a->abgr - b->abgr);
X}
X
Xstatic int abs(x)
X{
X	if (x < 0) return(-x);
X	return(x);
X}
X
X/*
X * Depending on how the images will be displayed, we may wish to
X * change the colour maps in our images into a more convenient form.
X * Lrectwrite() is the default, but writeRGB would rather have 3
X * tables.  For screens with colour maps, we can either map the
X * images colour map into the screen colour map or load the screen
X * colour map with the image colour map.  In the latter case we'll
X * want to compress the colourmap as much as possible to minimize
X * on-screen weirdness.  It would also be nice to put the image
X * colours into screen map entries that won't change a lot when
X * they are re-loaded.  We could also change the image data to
X * avoid a colourmap to colourmap conversion, but it probably isn't
X * worth it.
X *
X * This should really be 3 or 4 separate functions :-(
X */
Xvoid adjust_colourmap(img)
Xstruct mem_image*	img;
X{
X	int i, r, g, b;
X	int mindiff, best;
X	unsigned char* red;
X	unsigned char* green;
X	unsigned char* blue;
X	int new;
X	char* p;
X	int	workspace[256];
X	struct map_entry map[256];
X
X	if (disp_mode == RGB_LRECT)
X		return;
X
X	if (disp_mode == RGB_WRITE) {
X		/* do it for writeRGB */
X		for (i = 0; i < img->maplen; i++)
X			workspace[i] = img->colourmap[i];
X		red = (unsigned char*)img->colourmap;
X		green = (unsigned char*)img->colourmap + img->maplen;
X		blue = (unsigned char*)img->colourmap + img->maplen * 2;
X		for (i = 0; i < img->maplen; i++) {
X			*red++ = workspace[i] & 255;
X			*green++ = (workspace[i] >> 8) & 255;
X			*blue++ = (workspace[i] >> 16) & 255;
X		}
X		return;
X	}
X
X	/*img->mapmap = (int*)malloc(img->maplen * sizeof(int));*/
X	/* Don't necessarily need 256, but ... */
X	img->mapmap = (int*)malloc(256 * sizeof(int));
X	if (img->mapmap == NULL)
X		no_mem();
X
X	/* colourmapped display */
X	if (disp_mode == CMAP_NEWS) {
X		if (!dither) {
X			for (i = 0; i < img->maplen; i++) {
X				img->mapmap[i] = rgb2newsmap(
X					r = img->colourmap[i] & 255,
X					g = (img->colourmap[i] >> 8) & 255,
X					b = (img->colourmap[i] >> 16) & 255
X				);
X			}
X		}
X		else {
X			/* we're going to change all the indicies anyways... */
X			for (i = 0; i < 256; i++)
X				img->mapmap[i] = i;
X			init_floydstein(img);
X		}
X		return;
X	}
X
X	/* disp_mode == CMAP_TRUE */
X
X	/* A little kludge here.  The basic idea is that all the images
X	 * which use a global colourmap should have the same mapmap.  What
X	 * is not so pretty is that maybe the first image didn't use the
X	 * global map.
X	 */
X	if (img->imf->imglist != img &&
X		img->imf->imglist->colourmap == img->colourmap) {
X		int qq;
X
X		img->mapmap = img->imf->imglist->mapmap;
X		for (i = 0; i < 256; i++)
X			colour_used[i] = 0;
X
X		for (i = 0; i < 256; i++)
X			if (img->mapmap[i] >= 0)
X				colour_used[img->mapmap[i]] = 1;
X		return;
X	}
X
X	for (i = 0; i < 256; i++) {
X		img->mapmap[i] = -1;
X		colour_used[i] = 0;
X	}
X
X#ifdef OLD
X	/* disp_mode == CMAP_TRUE */
X	/* ok, let's crank */
X	for (i = 0; i < img->maplen; i++) {
X		map[i].orig_index = i;
X		map[i].abgr = img->colourmap[i];
X		map[i].screen_index = -1;
X		map[i].used = 0;
X	}
X	
X	for (p = img->data, i = img->width * img->height; i > 0; i--)
X		map[*p++].used = 1;
X	
X	qsort(map, img->maplen, sizeof(struct map_entry), map_ent_cmp);
X
X	for (i = 1; i < img->maplen; i++) {
X		if (!map[i].used)
X			break;			/* 'cause all the unused ones are at the end */
X		if (map[i].abgr == map[i - 1].abgr)
X			map[i].used = -1;
X	}
X
X	for (i = 0; i < 256; i++)
X		workspace[i] = 0;	/* i.e., we haven't used this screen colour */
X	
X	for (i = 0; i < img->maplen; i++) {
X		if (!map[i].used)
X			break;
X		if (map[i].used == -1)
X			continue;
X		new = rgb2newsmap(map[i].abgr & 255,
X			(map[i].abgr >> 8) & 255,
X			(map[i].abgr >> 16) & 255
X		);
X		if (workspace[new] == 0) {
X			workspace[new] = 1;
X			map[i].screen_index = new;
X		}
X	}
X
X	new = 0;
X	for (i = 0; i < img->maplen; i++)
X		if (map[i].used && map[i].screen_index == -1)
X			new++;
X	printf("%d hard colours to map\n", new);
X
X#ifdef SIMPFIT
X	/*
X	 * allocate entries for the guys who collided; I should do some sort of
X	 * best fitting, but for now just start from the top.  The only best
X	 * fit algorithm I can think of is rather expensive.
X	 */
X	new = 255;
X	for (i = 0; i < img->maplen; i++) {
X		if (!map[i].used)
X			break;
X		if (map[i].screen_index != -1)
X			continue;
X		while (workspace[new])
X			new--;
X		workspace[new] = 1;
X		map[i].screen_index = new;
X	}
X#endif
X
X	for (i = 0; i < img->maplen; i++) {
X		int d;
X		short r,g,b;
X		if (!map[i].used)
X			break;
X		if (map[i].screen_index != -1)
X			continue;
X		best = 0;
X		mindiff = 10000;
X		for (new = 0; new < 256; new++) {
X			if (workspace[new])
X				continue;
X			getmcolor(new, &r, &g, &b);
X			d = abs(r - ((map[i].abgr & 255))) +
X				abs(g - ((map[i].abgr >> 8) & 255)) +
X				abs(b - ((map[i].abgr >> 16) & 255));
X			if (d < mindiff) {
X				best = new;
X				mindiff = d;
X			}
X		}
X		workspace[best] = 1;
X		map[i].screen_index = best;
X	}
X
X	/* finally, re-define the image's colourmap.  Don't forget about those
X	 * duplicate entries.
X	 */
X	for (i = 0; i < img->maplen; i++)
X		img->mapmap[i] = -1;
X
X	for (i = 0; i < img->maplen; i++) {
X		if (!map[i].used)
X			break;
X		if (map[i].used == -1)
X			img->mapmap[map[i].orig_index] = 
X				img->mapmap[map[i - 1].orig_index];
X		else
X			img->mapmap[map[i].orig_index] = map[i].screen_index;
X	}
X#endif
X}
X
Xstatic short orig_colourmap[256 * 3];
Xstatic int orig_cmap_inst = 1;
X
Xtrue_cmap(img, line)
Xstruct mem_image*	img;
Xint					line;
X{
X	register unsigned char* p;
X	register int			i;
X	register int			try;
X	short					r,g,b;
X
X	for (p = img->data + img->width * line, i = img->width; i >= 0; p++, i--) {
X		if (img->mapmap[*p] >= 0)
X			continue;
X		try = rgb2newsmap(r = img->colourmap[*p] & 255,
X			g = (img->colourmap[*p] >> 8) & 255,
X			b = (img->colourmap[*p] >> 16) & 255
X		);
X		if (colour_used[try]) {
X			int		mindiff, d, j;
X
X			try = 255;
X			mindiff = 10000;
X			for (j = 255; j >= 0; j--) {
X				if (colour_used[j])
X					continue;
X				if ((d = abs(r - orig_colourmap[j * 3]) +
X						 abs(g - orig_colourmap[j * 3 + 1]) +
X						 abs(b - orig_colourmap[j * 3 + 2])) < mindiff) {
X					try = j;
X					mindiff = d;
X				}
X			}
X		}
X		img->mapmap[*p] = try;
X		colour_used[try] = 1;
X		if (!orig_cmap_inst && img->imf == cur_imf)
X			mapcolor(try, r, g, b);
X	}
X}
X
Xvoid save_colourmap()
X{
X	short*	p;
X	int		i;
X
X	for (i = 0, p = orig_colourmap; i < 256; i++, p += 3)
X		getmcolor(i, p, p + 1, p + 2);
X}
X
Xvoid set_colourmap(img)
Xstruct mem_image*	img;
X{
X	int	i;
X
X	for (i = 0; i < img->maplen; i++)
X		if (img->mapmap[i] >= 0)
X			mapcolor(img->mapmap[i],
X				img->colourmap[i] & 255,
X				(img->colourmap[i] >> 8) & 255,
X				(img->colourmap[i] >> 16) & 255
X			);
X	orig_cmap_inst = 0;
X}
X
Xvoid toggle_colourmap(img)
Xstruct mem_image*	img;
X{
X	if (orig_cmap_inst)
X		set_colourmap(img);
X	else
X		restore_colourmap();
X}
X
Xvoid restore_colourmap()
X{
X	short*	p;
X	int		i;
X
X	for (i = 0, p = orig_colourmap; i < 256; i++, p += 3)
X		mapcolor(i, p[0], p[1], p[2]);
X	orig_cmap_inst = 1;
X}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0