[comp.graphics] A GIF to RLE converter

koblas@mips.COM (David Koblas) (01/16/89)

Since this is probably of general interest to more than one or two people,
I'm posting this.  For a description of what RLE is and what the Utah
Raster Toolkit is I'll point you at a previous posting with included
the BLURB.UTAH file.

If you find any bugs or have any great ideas on how to improve this
I'd be greatly interested...etc, and so forth.

One word of warning, many GIF images that I've found do not fully
conform to the GIF standard.  They are lacking a NULL byte at the 
end if the compressed data stream.

--
name : David Koblas                  uucp  : {ames,decwrl}!mips!koblas 
place: MIPS Computers Systems        domain: koblas@mips.com
quote: I haven't lost my mind -- it's backed up on tape somewhere.

------CUT------HERE---------CUT------HERE----------CUT------HERE-------
#! /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:  giftorle.1 giftorle.c Makefile
# Wrapped by koblas@giant on Sun Jan 15 23:22:19 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'giftorle.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'giftorle.1'\"
else
echo shar: Extracting \"'giftorle.1'\" \(833 characters\)
sed "s/^X//" >'giftorle.1' <<'END_OF_FILE'
X.\" Copyright (c) 1989, David Koblas
X.TH GIFTORLE 1
X.UC 4 
X.SH NAME
giftorle \- Convert GIF images to RLE format
X.SH SYNOPSIS
X.B giftorle
X[
X.B \-o
X.I outfile
X]
X[
X.B \-c
X]
X[
X.I infile
X]
X.SH DESCRIPTION
X.I Giftorle
converts a file from Graphics Interchange Format (GIF) format into RLE format.
Images converted with 
X.I giftorle
will need to be fliped with
X.IR rleflip -v
for correct presentation.
If no input file is specified, the data is read from stdin.
The following options are available:
X.TP
X.B -c
Preserve the colormap that the GIF image contains, otherwise the
colormap is applied to input image.
X.TP
X.B -o
Use 
X.I outputfile 
as output instead of 
X.I stdout.
X
X.LP
X.SH MISC
GIF and Graphics Interchange Format are both trademarks of CompuServe
Incorporated.
X
X.SH AUTHOR
David Koblas (koblas@mips.com or koblas@cs.uoregon.edu)
END_OF_FILE
if test 833 -ne `wc -c <'giftorle.1'`; then
    echo shar: \"'giftorle.1'\" unpacked with wrong size!
fi
# end of 'giftorle.1'
fi
if test -f 'giftorle.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'giftorle.c'\"
else
echo shar: Extracting \"'giftorle.c'\" \(12206 characters\)
sed "s/^X//" >'giftorle.c' <<'END_OF_FILE'
X/* +------------------------------------------------------------------+ */
X/* | Copyright 1989, David Koblas.                                    | */
X/* |   You may copy this file in whole or in part as long as you      | */
X/* |   don't try to make money off it, or pretend that you wrote it.  | */
X/* +------------------------------------------------------------------+ */
X
X#include	<stdio.h>
X#include	<sys/file.h>
X#include	"svfb_global.h"
X
X#ifndef lint
static char *rcsid = "$Header: giftorle.c,v 1.4 89/01/15 22:23:08 koblas Exp $";
X#endif
X
X#define	MAXCOLORMAPSIZE		256
X
X#define	TRUE	1
X#define	FALSE	0
X
X#define CM_RED		0
X#define CM_GREEN	1
X#define CM_BLUE		2
X
X#define	MAX_LWZ_BITS		12
X
X#define	ReadOK(file,buffer,len)	(fread(buffer,len,1,file)!=0)
X#define	EasyFail(str,status)	while(1){fprintf(stderr,str);return(status);}
X#define	HardFail(str,status)	while(1){fprintf(stderr,str);exit  (status);}
X
X#define LM_to_uint(a,b)			(((b)<<8)|(a))
X
X#define MY_NAME "giftorle"
X
struct {
X	unsigned int		Width;
X	unsigned int		Height;
X	unsigned char		ColorMap[3][MAXCOLORMAPSIZE];
X	unsigned int		BitPixel;
X	unsigned int		ColorResolution;
X	unsigned int		Background;
X} Screen;
X
static int output_colormap = FALSE;
X
main(argc,argv)
int	argc;
char	**argv;
X{
X	int		c;
X	extern int	optind;
X	extern char	*optarg;
X
X	while ((c=getopt(argc,argv,"co:"))!=EOF) {
X		switch (c) {
X		case 'c':
X			output_colormap = TRUE;
X			break;
X		case 'o':
X			if (freopen(optarg,"w",stdout)==NULL) {
X				fprintf(stderr,"Unable to open %s for output\n",optarg);
X				exit(1);
X			}
X			break;
X		default :
X			fprintf(stderr,"Usage: %s [-o filename] [file]\n",MY_NAME);
X			exit(1);
X		}
X	}
X
X	if (argc < (optind-1)) {
X		fprintf(stderr,"Usage: %s [-o filename] [file]\n",MY_NAME);
X		exit(1);
X	}
X
X	if (argc == optind)
X		ReadGIF(NULL);
X
X	while (optind!=argc) {
X		ReadGIF(argv[optind]);
X		optind++;
X	}
X}
X
ReadGIF(filename)
char	*filename;
X{
X	unsigned char	buf[16];
X	unsigned char	c;
X	unsigned char	LocalColorMap[3][MAXCOLORMAPSIZE];
X	FILE			*fd;
X	int				use_global_colormap;
X	int				bit_pixel;
X	int				count=0;
X
X	if (filename==NULL)
X		fd=stdin ;
X	else
X		if ((fd=fopen(filename,"r"))==NULL) {
X			fprintf(stderr,"Couldn't open file %s for input\n",filename);
X			return ;
X		}
X
X	if (! ReadOK(fd,buf,6))
X		EasyFail("error reading magic number\n",TRUE);
X	if (strncmp(buf,"GIF87a",6)!=0)
X		EasyFail("bad magic number (version mismatch?)\n",TRUE);
X	if (! ReadOK(fd,buf,7))
X		EasyFail("error reading screen descriptor\n",TRUE);
X	Screen.Width           = LM_to_uint(buf[0],buf[1]);
X	Screen.Height          = LM_to_uint(buf[2],buf[3]);
X	Screen.BitPixel        = 2<<(buf[4]&0x07);
X	Screen.ColorResolution = (((buf[4]&0x70)>>3)+1);
X	Screen.Background      = buf[5];
X	if ((buf[4]&0x80)==0x80) {
X		if (ReadColorMap(fd,Screen.BitPixel,Screen.ColorMap))
X			return (TRUE);
X	}
X
X	while (1) {
X		if (! ReadOK(fd,&c,1))
X			EasyFail("No image data -- EOF\n",TRUE);
X		if (c == ';') 
X			return FALSE;
X		if (c == '!') {
X			if (! ReadOK(fd,&c,1))
X				EasyFail("No extention function code -- EOF\n",TRUE);
X			if (IgnoreExtention(fd))
X				return(TRUE);
X		}
X		if (c != ',') {
X			fprintf(stderr,"Bogus character ignoring '%c'\n",c);
X			continue;
X		}
X		if (count == 1)
X			HardFail("This file contains more than one image! FAILING\n",1);
X		count++;
X
X		if (! ReadOK(fd,buf,9))
X			EasyFail("Couldn't read left/top/width/height\n",TRUE);
X		if ((buf[8]&0x80)==0x80)
X			use_global_colormap = FALSE ;
X		else
X			use_global_colormap = TRUE ;
X
X		bit_pixel = 1<<((buf[8]&0x07)+1);
X
X		if (! use_global_colormap) {
X			if (ReadColorMap(fd,bit_pixel,LocalColorMap))
X				return TRUE;
X		}
X
X		if ((buf[8]&0x40)==0x40) {
X			if (ReadInterlaced(fd,LM_to_uint(buf[4],buf[5]),
X					LM_to_uint(buf[6],buf[7]),
X					use_global_colormap?Screen.ColorMap:LocalColorMap))
X				return TRUE;
X		} else {
X			if (ReadRaster(fd,LM_to_uint(buf[4],buf[5]),
X					LM_to_uint(buf[6],buf[7]),
X					use_global_colormap?Screen.ColorMap:LocalColorMap))
X				return TRUE;
X		}
X	}
X}
X
ReadColorMap(fd,number,buffer)
XFILE			*fd;
int				number;
unsigned char	buffer[3][MAXCOLORMAPSIZE];
X{
X	int				i;
X	unsigned char	rgb[3];
X
X	for (i=0;i<number;i++) {
X		if (! ReadOK(fd,rgb,sizeof(rgb)))
X			EasyFail("Bogus colormap\n",TRUE);
X		buffer[CM_RED][i] = rgb[0] ;
X		buffer[CM_GREEN][i] = rgb[1] ;
X		buffer[CM_BLUE][i] = rgb[2] ;
X	}
X	return FALSE;
X}
X
IgnoreExtention(fd)
XFILE	*fd;
X{
X	static char		buf[256];
X	unsigned char	c;
X
X	while (1) {
X		if (! ReadOK(fd,&c,1))
X			EasyFail("EOF in extention\n",TRUE);
X		if (c == 0)
X			return FALSE;
X		if (read(fd,buf,(int) c)!=(int) c) 
X			EasyFail("EOF in extention\n",TRUE);
X	}
X}
X
GetCode(fd, code_size, flag)
XFILE	*fd;
int		code_size;
int		flag;
X{
X    static unsigned char	buf[280];
X	static int				curbit,lastbit,done,last_byte;
X    int						i, j, k, ret;
X	unsigned char			count;
X
X	if (flag) {
X		curbit = 0;
X		lastbit = 0;
X		done = FALSE;
X		return 0;
X	}
X
X	if ( (curbit+code_size) >= lastbit) {
X		if (done) {
X			if (curbit>=lastbit)
X				EasyFail("Ran off the end of my bits\n",-1);
X		}
X		buf[0] = buf[last_byte-2];
X		buf[1] = buf[last_byte-1];
X		if (! ReadOK(fd,&count,1)) {
X			EasyFail("Error in getting buffer size\n",-1);
X		}
X		if (count == 0) {
X			done = TRUE;
X		} else if (! ReadOK(fd,&buf[2],count))
X			EasyFail("Error in getting buffer\n",-1);
X		last_byte = 2 + count;
X		curbit = (curbit - lastbit) + 16;
X		lastbit = (2+count)*8 ;
X    }
X
X    ret = 0;
X    for( i = curbit, j = 0; j < code_size; i++, j++ ) 
X		ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
X
X	curbit += code_size;
X
X    return ret;
X}
X
LWZReadByte(fd,flag,input_code_size)
XFILE	*fd;
int		flag;
int		input_code_size;
X{
X	static int		fresh=FALSE;
X	int				code,incode;
X	static int		code_size,set_code_size;
X	static int		max_code,max_code_size;
X	static int		firstcode,oldcode;
X	static int		clear_code,end_code;
X	static int		table[2][(1<< MAX_LWZ_BITS)];
X	static int		stack[(1<<(MAX_LWZ_BITS))*2],*sp;
X	register int	i;
X
X	if (flag) {
X		set_code_size = input_code_size;
X		code_size = set_code_size+1;
X		clear_code = 1 << set_code_size ;
X		end_code = clear_code + 1;
X		max_code_size = 2*clear_code;
X		max_code = clear_code+2;
X
X		GetCode(fd,NULL,NULL,NULL,TRUE);
X		
X		fresh=TRUE;
X
X		for (i=0;i<clear_code;i++) {
X			table[0][i] = 0;
X			table[1][i] = i;
X		}
X		for (;i<(1<<MAX_LWZ_BITS);i++)
X			table[0][i] = table[1][0] = 0;
X
X		sp=stack;
X
X		return 0;
X	} else if (fresh) {
X		fresh = FALSE;
X		do {
X			firstcode=oldcode=
X				GetCode(fd, code_size, FALSE);
X		} while (firstcode == clear_code);
X		return firstcode;
X	}
X
X	if (sp > stack) 
X		return *--sp;
X
X	while ((code=GetCode(fd,code_size,FALSE))>=0) {
X		if (code == clear_code) {
X			for (i=0;i<clear_code;i++) {
X				table[0][i] = 0;
X				table[1][i] = i;
X			}
X			for (;i<(1<<MAX_LWZ_BITS);i++)
X				table[0][i] = table[1][i] = 0;
X			code_size = set_code_size+1;
X			max_code_size = 2*clear_code;
X			max_code = clear_code+2;
X			sp=stack; 
X			firstcode=oldcode=
X					GetCode(fd,code_size,FALSE);
X			return firstcode;
X		} else if (code == end_code) {
X			unsigned char		count;
X			unsigned char		junk;
X
X			while (ReadOK(fd,&count,1) && (count!=0))
X				while (count-->0 && ReadOK(fd,&junk,1));
X			if (count!=0)
X				EasyFail("missing EOD in data stream (common occurance)\n",-3);
X			return -2;
X		}
X
X		incode = code;
X
X		if (code >= max_code) {
X			*sp++ = firstcode;
X			code = oldcode;
X		}
X
X		while (code >= clear_code) {
X			*sp++ = table[1][code];
X			if (code == table[0][code])
X				EasyFail("Circular table entry BIG ERROR\n",-1);
X			code = table[0][code];
X		}
X
X		*sp++ = firstcode = table[1][code];
X
X		if ((code=max_code)<(1<<MAX_LWZ_BITS)) {
X			table[0][code] = oldcode;
X			table[1][code] = firstcode;
X			max_code++;
X			if ((max_code >= max_code_size) && 
X				(max_code_size < (1<<MAX_LWZ_BITS))) {
X				max_code_size *= 2;
X				code_size++;
X			}
X		}
X
X		oldcode = incode;
X
X		if (sp > stack) 
X			return *--sp;
X	}
X	return code;
X}
X
ReadInterlaced(fd,len,height,cmap)
XFILE	*fd;
int		len,height;
char	cmap[3][MAXCOLORMAPSIZE];
X{
X	unsigned char		c;	
X	int					v;
X	int					xpos=0;
X	rle_pixel			**scanline[3];
X	rle_pixel			*ptr[3] ;
X	struct sv_globals	global ;
X	int					i,j;
X	int					ypos=0,pass=0;
X	static rle_map		out_map[3*(1<<8)];
X
X
X	for (j=0;j<(output_colormap?1:3);j++) {
X		if ((scanline[j]=(rle_pixel **)
X						 malloc(height*sizeof(rle_pixel *)))==NULL)
X				EasyFail("Unable to malloc space for pixels #1\n",1);
X		for (i=0;i<height;i++) {
X			if ((scanline[j][i]=(rle_pixel *)
X								malloc(len*sizeof(rle_pixel)))==NULL) {
X				for (;j>=0;j--)
X					for (;i>=0;i--)
X						if (scanline[j][i]!=NULL) free(scanline[i]);
X				EasyFail("Unable to malloc space for pixels #2\n",1);
X			}
X		}
X	}
X
X	global = sv_globals;
X
X	if (output_colormap) {
X		global.sv_ncolors =  1;
X		global.sv_ncmap = 3;
X		global.sv_cmaplen = 8;
X		global.sv_cmap = out_map;
X		for (i=0;i<(1<<8);i++) {
X			out_map[i+(0<<8)] = cmap[CM_RED][i] << 8;
X			out_map[i+(1<<8)] = cmap[CM_GREEN][i] << 8;
X			out_map[i+(2<<8)] = cmap[CM_BLUE][i] << 8;
X		}
X	} else {
X		SV_SET_BIT(global, SV_RED);
X		SV_SET_BIT(global, SV_GREEN);
X		SV_SET_BIT(global, SV_BLUE);
X		global.sv_ncolors =  3;
X	}
X	global.svfb_fd = stdout ;
X	global.sv_xmax = len - 1;
X	global.sv_ymax = height - 1;
X
X	sv_setup(RUN_DISPATCH, &global);
X
X	if (! ReadOK(fd,&c,1))
X		EasyFail("Bogus image data -- EOF\n",TRUE);
X	if (LWZReadByte(fd,TRUE,c)<0)
X		return TRUE;
X
X	while ((v=LWZReadByte(fd,FALSE,c))>=0) {
X		if (output_colormap) {
X			scanline[SV_RED][ypos][xpos]   = v ;
X		} else {
X			scanline[SV_RED][ypos][xpos]   = cmap[CM_RED][v] ;
X			scanline[SV_GREEN][ypos][xpos] = cmap[CM_GREEN][v] ;
X			scanline[SV_BLUE][ypos][xpos]  = cmap[CM_BLUE][v] ;
X		}
X		xpos++;
X		if (xpos==len) {
X			xpos = 0;
X			switch (pass) {
X			case 0: 
X			case 1:
X				ypos += 8; break;
X			case 2:
X				ypos += 4; break;
X			case 3:
X				ypos += 2; break;
X			}
X			if (ypos >= height) {
X				pass++;
X				switch (pass) {
X				case 1:
X					ypos = 4; break;
X				case 2:
X					ypos = 2; break;
X				case 3:
X					ypos = 1; break;
X				}
X			}
X		}
X	}
X
X	for (i=0;i<height;i++) {
X		ptr[0] = scanline[SV_RED][i] ;
X		if (! output_colormap) {
X			ptr[1] = scanline[SV_GREEN][i] ;
X			ptr[2] = scanline[SV_BLUE][i] ;
X		}
X		sv_putrow(ptr,len,&global);
X	}
X
X	sv_puteof(&global);
X
X	for (i=0;i<height;i++) {
X		if (scanline[0][i]!=NULL) free(scanline[0][i]);
X		if (output_colormap) continue;
X
X		if (scanline[1][i]!=NULL) free(scanline[1][i]);
X		if (scanline[2][i]!=NULL) free(scanline[2][i]);
X	}
X	free(scanline[0]);
X	if (! output_colormap) {
X		free(scanline[1]); 
X		free(scanline[2]);
X	}
X
X	if (v==(-2))
X		return FALSE;
X	return TRUE;
X}
X
ReadRaster(fd,len,height,cmap)
XFILE	*fd;
int		len,height;
char	cmap[3][MAXCOLORMAPSIZE];
X{
X	unsigned char		c;	
X	int					v;
X	int					xpos=0;
X	rle_pixel			*scanline[3];
X	struct sv_globals	global ;
X	int					i;
X	static rle_map		out_map[3*(1<<8)];
X
X	if (((scanline[0]=(rle_pixel *)malloc(len*sizeof(rle_pixel)))==NULL) ||
X	    ((scanline[1]=(rle_pixel *)malloc(len*sizeof(rle_pixel)))==NULL) ||
X	    ((scanline[2]=(rle_pixel *)malloc(len*sizeof(rle_pixel)))==NULL))
X		EasyFail("Unable to malloc space for pixels\n",1);
X
X	global = sv_globals;
X
X	if (output_colormap) {
X		global.sv_ncolors =  1;
X		global.sv_ncmap = 3;
X		global.sv_cmaplen = 8;
X		global.sv_cmap = out_map;
X		for (i=0;i<(1<<8);i++) {
X			out_map[i+(0<<8)] = cmap[CM_RED][i] << 8;
X			out_map[i+(1<<8)] = cmap[CM_GREEN][i] << 8;
X			out_map[i+(2<<8)] = cmap[CM_BLUE][i] << 8;
X		}
X	} else {
X		SV_SET_BIT(global, SV_RED);
X		SV_SET_BIT(global, SV_GREEN);
X		SV_SET_BIT(global, SV_BLUE);
X		global.sv_ncolors =  3;
X	}
X	global.svfb_fd = stdout ;
X	global.sv_xmax = len - 1;
X	global.sv_ymax = height - 1;
X
X	sv_setup(RUN_DISPATCH, &global);
X
X	if (! ReadOK(fd,&c,1))
X		EasyFail("Bogus image data -- EOF\n",TRUE);
X	if (LWZReadByte(fd,TRUE,c)<0)
X		return TRUE;
X
X	while ((v=LWZReadByte(fd,FALSE,c))>=0) {
X		if (output_colormap) {
X			scanline[SV_RED][xpos]   = v ;
X		} else {
X			scanline[SV_RED][xpos]   = cmap[CM_RED][v] ;
X			scanline[SV_GREEN][xpos] = cmap[CM_GREEN][v] ;
X			scanline[SV_BLUE][xpos]  = cmap[CM_BLUE][v] ;
X		}
X		xpos++;
X		if (xpos==len) {
X			sv_putrow(scanline,len,&global);
X			xpos=0;
X		}
X	}
X
X	if (xpos!=0) 
X			sv_putrow(scanline,xpos,&global);
X
X	sv_puteof(&global);
X
X	free(scanline[0]);
X	free(scanline[1]);
X	free(scanline[2]);
X
X	if (v==(-2))
X		return FALSE;
X	return TRUE;
X}
END_OF_FILE
if test 12206 -ne `wc -c <'giftorle.c'`; then
    echo shar: \"'giftorle.c'\" unpacked with wrong size!
fi
# end of 'giftorle.c'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(237 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
RLELIB = ../lib/librle.a
X
CFLAGS = -g -I../utah/include
X
SHAR = /user/koblas/bin/shar
X
CSRC = giftorle.c
MAN  = giftorle.1
X
giftorle : giftorle.o
X	$(CC) $(CFLAGS) -o $@ giftorle.o $(RLELIB)
X
shar:
X	$(SHAR) $(MAN) $(CSRC) Makefile > Shar
END_OF_FILE
if test 237 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
echo shar: End of shell archive.
exit 0

-- 
name : David Koblas                  uucp  : {ames,decwrl}!mips!koblas 
place: MIPS Computers Systems        domain: koblas@mips.com
quote: I haven't lost my mind -- it's backed up on tape somewhere.