./                                                                                                     755    3760       0            0  4446542150   4161                                                                                                                                                                                                                                                                                                                                                                      ./gif2sun.c                                                                                            644    3760       1        23332  4446542147   6021                                                                                                                                                                                                                                                                                                                                                                      /*
  gif2sun.c -- convert GIF picture files to Sun rasterfile format

  runs on Sun 3/4 -- not 386 -- byteorder must be Motorola-style

  version 1.1

  note: bug fixed in extension reading code.

  Timo Rossi  1989 --  converted from gif2iff.c converted from agif.c
*/

#include <stdio.h>
#include <string.h>
#include <rasterfile.h>

char *malloc();

typedef char BYTE;
typedef unsigned char UBYTE;
typedef short WORD;
typedef unsigned short UWORD;
typedef long LONG;
typedef unsigned long ULONG;

#define TRUE 1
#define FALSE 0

/* GIF stuff */

#define GIF_SIGNATURE	"GIF87a"	/* GIF file identifier */

#define IMAGE_SEPARATOR      ','
#define EXTENSION_INTRODUCER '!'
#define GIF_TERMINATOR       ';'

struct ScreenDescriptor {
  UWORD Width,Height;
  UBYTE PixelFlags;
  UBYTE Background;
  UBYTE NotUsed;
};

struct ImageDescriptor {
  UWORD Left,Top;
  UWORD Width,Height;
  UBYTE PixelFlags;
};

/*
  sizeof(struct ScreenDescriptor) or sizeof(struct Imagedescriptor)
  can't be used when reading from file because of structure alignment
  the following values are used
 */
#define SCR_DESCR_LENGTH 7
#define IMG_DESCR_LENGTH 9

#define MAX_PLANES 8
#define MAX_COLORS (1<<MAX_PLANES)

UBYTE *pixmap;
int  width,height,width2,depth;

UBYTE color_map[3*MAX_COLORS];
int color_map_used=0;

FILE *fp;

char *fname;	/* for fatal_error() */

/* LZW decompression stuff */

struct tabentry {
  unsigned next;
  char ch;
};

#define MAXMAX    4096
#define MAXBITS     12
#define STACKSIZE 4000

#define push(x) { \
 *stackpointer++=(x); \
 if(stackpointer>=&stack[STACKSIZE]) \
     fatal_error("stack overflow"); }

#define pop() (*--stackpointer)

/* global variables for decompressing routine */

int code_size,clear_code,nbits;
int byte_count,ch,bt,end_of_data;

#define END_CODE (clear_code+1)
#define FIRST    (clear_code+2)

struct tabentry table[MAXMAX+10];
char stack[STACKSIZE+10];

/* function declarations */

void read_gif();
void read_image();

void decompress_image();
int  read_code();

void fatal_error();

void init_image();
void put_pixel();

void read_file();
int  read_char();
void seek_file();
void write_file();
void write_long();

char *xmalloc();

void save_raster();

void flip_word();

/*
  the main program
 */
void main(argc,argv)
int argc;
char *argv[];
{
 if(argc>3)
   {
     fprintf(stderr,"Usage: gif2sun [inputfile [outputfile]]\n");
     exit(1);
   }

 fname=( argc>=2 ? argv[1] : NULL );
 read_gif(fname);

 fname=( argc==3 ? argv[2] : NULL );
 save_raster(fname);

 exit(0);
}

/*
  display error message & exit
 */
void fatal_error(errmsg)
char *errmsg;
{
 fprintf(stderr,"gif2sun: ");
 fprintf(stderr,errmsg,fname);
 putc('\n',stderr);
 exit(1);
}

/*
  the main GIF-picture reading routine

  parses the structure of the GIF file, calls read_image()
  for all images in GIF file
 */
void read_gif(name)
char *name;
{
 char id[6];
 struct ScreenDescriptor scrdesc;
 int n,c,ncolors;
 int keepgoing=TRUE;

 if(name)
   {
     if((fp=fopen(name,"r"))==NULL)
       fatal_error("can't open file '%s'");
   }
 else fp=stdin;

 read_file(fp,id,6);
 if(strncmp(id,GIF_SIGNATURE,6))
   {
     if(name) fatal_error("'%s' is not a GIF file");
     else fatal_error("stdin: not GIF format");
   }
 read_file(fp,(char *)&scrdesc,SCR_DESCR_LENGTH);
 flip_word(&scrdesc.Width);
 flip_word(&scrdesc.Height);
 depth=(scrdesc.PixelFlags & 7) + 1;

 if(scrdesc.PixelFlags & 0x80) /* global color map */
   {
     ncolors= 1<<depth;
     if(ncolors>MAX_COLORS) ncolors=MAX_COLORS;
     read_file(fp,(char *)color_map,3*ncolors);
     color_map_used=1;
   }

 width=scrdesc.Width;
 height=scrdesc.Height;
 width2=(width+1)&(-2); /* even width */
 pixmap=(UBYTE *)xmalloc(width2*height);

 while(keepgoing) /* loop all images in a file */
   {
     while((c=read_char(fp))!=EOF && c!=IMAGE_SEPARATOR &&
       c!=EXTENSION_INTRODUCER &&
         c!=GIF_TERMINATOR); /* skip all other bytes */
     switch(c)
       {
        case IMAGE_SEPARATOR:
	 read_image();
	 break;
	case EXTENSION_INTRODUCER:	/* skip extension */
	 (void)read_char(fp); /* skip function code */
	 while((n=read_char(fp)) && n!=EOF)
	   seek_file(fp,(long)n,1);
	 break;
        case GIF_TERMINATOR:
       case EOF:
	 keepgoing=FALSE;
	 break;
       }
   }
 if(name) fclose(fp);
}

/*
  read one image in GIF file
  reads image descriptor, calls decompress_image()
  to read the image
 */
void read_image()
{
 struct ImageDescriptor imdesc;

 read_file(fp,(char *)&imdesc,IMG_DESCR_LENGTH);
 flip_word(&imdesc.Left);
 flip_word(&imdesc.Top);
 flip_word(&imdesc.Width);
 flip_word(&imdesc.Height);

 if(imdesc.PixelFlags & 0x80)
   {
	/* This program cannot really handle local color maps
	   but it can skip them anyway.
	 */
     fprintf(stderr,"gif2sun: skipping local color map\n");
     seek_file(fp,(long)(3*(1 << ( (imdesc.PixelFlags & 7) + 1))),1);
   }

 init_image(
   (int)imdesc.Left,(int)imdesc.Top,(int)imdesc.Width,(int)imdesc.Height,
	    (int)(imdesc.PixelFlags&0x40));

 decompress_image();
}

/*
  decompresses LZW-compressed image
  calls put_pixel

  note that pixels are compressed, not bytes
 */
void decompress_image()
{
 int k,fin_char;
 register int cur_code,max_code;
 register int old_code,in_code,free_code;
 register char *stackpointer=stack;

 code_size=read_char(fp);
 byte_count=read_char(fp);
 clear_code=1<<code_size;
 end_of_data=FALSE;
 bt=0;

 nbits=code_size+1;
 max_code=1<<nbits;
 free_code=FIRST;

 for(;;)  /* decompressor main loop */
   {
     cur_code=read_code();
     if(cur_code==END_CODE) return;
     if(cur_code==clear_code)
       {
	 nbits=code_size+1;
	 max_code=1<<nbits;
	 free_code=FIRST;
	 fin_char=k=old_code=cur_code=read_code();
	 put_pixel(k);
       }
     else
       {
	 in_code=cur_code;
	 if(cur_code>=free_code)
	   {
	     cur_code=old_code;
	     push(fin_char);
	   }
	 while(cur_code>=clear_code)
	   {
	     push(table[cur_code].ch);
	     cur_code=table[cur_code].next;
	   }
	 k=fin_char=cur_code;
	 push(k);

	  /* output pixels from stack in reverse order */
	 while(stackpointer>stack) put_pixel(pop());

	 table[free_code].ch=k;  /* add code */
	 table[free_code].next=old_code;
	 if(++free_code>=max_code)
	   {
	     if(nbits<MAXBITS)
	       {
		 nbits++;
		 max_code<<=1;
	       }
	   }
	 old_code=in_code;
       }
   }
}

/*
  reads one LZW-compression code from GIF-file
  note data block format and bit & byte order
 */
int read_code()
{
 register int code=0,bb,bl=nbits;

 static int masks[]={ 0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff };

 if(end_of_data) return END_CODE;

 while(bl)
  {
   if(bt==0)
    {
     ch=read_char(fp);
     if(ch==EOF) return END_CODE;
     byte_count--;
     if(byte_count==0)
      {
	byte_count=read_char(fp);
	if(byte_count==0 && byte_count==EOF)
	  {
	   end_of_data=TRUE;
	   break;
	  }
      }
     bt=8;
    }

   bb= bt < bl ? bt : bl; /* min(bt,bl) */
   code |= (((ch >> (8-bt))&masks[bb]) << (nbits-bl));
   bl-=bb;
   bt-=bb;
  }
 return code;
}

/*
  swaps the low and high bytes in a 16-bit word
  this is necessary because GIF uses lo-byte/hi-byte order.
 */
void flip_word(ptr)
UWORD *ptr;
{
 register char a,*c=(char *)ptr;

 a=c[0];c[0]=c[1];c[1]=a;
}

/*
 global variables for image output
 */
int xpos,ypos,xstart,ystart,xend,yend,lacepass,no_more_pixels;
long planepos;

/*
  pixel output initializing routine
 */
void init_image(left,top,width,height,lace)
int left,top,width,height,lace;
{
 xpos=xstart=left;
 ypos=ystart=top;
 xend=left+width;
 yend=top+height;
 /* check if image fits on screen */
 if(xend>width || yend>height)
      fatal_error("illegal image dimensions");
 lacepass = lace ? 1 : 0;
 no_more_pixels=FALSE;
 planepos=width2*ypos+xpos;
}

/*
  pixel output routine
  handles GIF interlace scan line order
  ( note that GIF interlace has nothing to do with
    Amiga interlace display mode )
 */
void put_pixel(c)
register int c;
{

  /* arrays for decoding GIF interlace */
 static int lace_incr[]={ 0,8,8,4,2 }; /* first element of array not used */
 static int lace_strt[]={ 0,0,4,2,1 }; /* 2 first elements not used */

 if(no_more_pixels) return;

 pixmap[planepos++]=c;

 if(++xpos>=xend)
  {
   xpos=xstart;
   if(lacepass==0)
    {
      if(++ypos>=yend) no_more_pixels=TRUE;
    }
   else
    {
      if((ypos+=lace_incr[lacepass])>=yend)
       {
        if(lacepass<4) ypos=ystart+lace_strt[++lacepass];
	else no_more_pixels=TRUE;
       }
    }
   planepos=width2*ypos+xpos;
  }
}

/*
 The rasterfile save routine
 */
void save_raster(name)
char *name;
{
 FILE *fp;
 struct rasterfile header;
 register int i,j;

 if(name)
   {
     if((fp=fopen(name,"w"))==NULL)
       fatal_error("can't open output file '%s'");
   }
 else fp=stdout;

 header.ras_magic=RAS_MAGIC;
 header.ras_width=width2;
 header.ras_height=height;
 header.ras_depth=8;
 header.ras_length=width2*height;
 header.ras_type=RT_STANDARD;

 if(!color_map_used)
   {
     header.ras_maptype=RMT_NONE;
     header.ras_maplength=0;
   }
 else
   {
     header.ras_maptype=RMT_EQUAL_RGB;
     header.ras_maplength=3*MAX_COLORS;
   }

 write_file(fp,(char *)&header,sizeof(header));
 if(color_map_used)
   {
     for(i=0;i<3;i++)
       for(j=0;j<MAX_COLORS;j++)
	 fputc((char)color_map[3*j+i],fp);
   }
 write_file(fp,(char *)pixmap,header.ras_length);

 if(name) fclose(fp);
}

/*
 file IO routines (stdio calls are used)
 */
void read_file(file,buff,len)
FILE *file;
char *buff;
int len;
{
 if(fread(buff,len,1,file)!=1)
   fatal_error("file read error");
}

int read_char(file)
FILE *file;
{
 return getc(file);
}

void seek_file(file,pos,mode)
FILE *file;
long pos;
int mode;
{
 if(fseek(file,pos,mode)<0)
     fatal_error("file seek error");
}

void write_file(file,buff,len)
FILE *file;
char *buff;
int len;
{
 if(fwrite(buff,len,1,file)!=1)
   fatal_error("file write error");
}

char *xmalloc(size)
int size;
{
 char *p;

 if((p=malloc((unsigned)size))==NULL) fatal_error("out of memory");
 return p;
}
 read_image()
  for all images in GIF file
 */
void read_gif(name)
char *name;
{
 char id[6];
 struct ScreenDescriptor scrdesc;
 int n,c,ncolors;
 int keepgoing=TRUE;

 if(name)
   {
     if((fp=fopen(name,"r"))==NULL)
       fatal_error("can't open file '%s'");
   }
 else fp=stdin;

 read_fil./gif2sun.doc                                                                                          644    3760       1         2154  4446542150   6315                                                                                                                                                                                                                                                                                                                                                                      GIF2SUN version 1.1

 gif2sun is a small program to convert GIF pictures to sun rasterfiles
 output uses 8-bit sun rasterfiles, to display these on monochrome display
 they should be converted to 1-bit pictures using the program rasfilter8to1.
 also in most cases you should increase the size of the image using the
 program rastrepl (these program come with SunOS).

Usage:
 gif2sun          --  reads stdin, writes stdout
 gif2sun infile   --  reads infile, writes stdout
 gif2sun infile outfile -- reads infile, writes outfile

Examples:
 
 gif2sun gifpic.gif | rastrepl | rasfilter8to1 -d | screenload

this converts the gif picture to 8-bit sun rasterfile, doubles the image
x and y dimensions (1 pixel becomes 2x2 pixels) and converts the
picture to 1-bit monochrome format using dithering (the -d flag).
then the picture is displayed on the Sun console using the screenload-program.

if you have a Sun color display, the rasfilter-program can be left out
of the above pipe-sequence.

gif2sun handles gif pictures that have upto 256 colors (8 bits/pixel)


 Timo Rossi
 University of Jyvaskyla
 INTERNET: trossi@tukki.jyu.fi

)>=yend)
       {
        if(lacepass<4) ypos=ystart+lace_strt[++lacepass];
	else no_more_pixels=TRUE;
       }
    }
   planepos=width2*ypos+xpos;
  }
}

/*
 The rasterfile save routine
 */
void save_raster(name)
char *name;
{
 FILE *fp;
 struct rasterfile header;
 register int i,j;

 if(name)
   {
     if((fp=fopen(name,"w"))==NULL)
       fatal_error("can't open output file '%s'");
   }
 else fp=st                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                ecompress_image()
{
 int k,fin_char;
 register int cur_code,max_code;
 register int old_code,in_code,free_code;
 register char *stackpointer=stack;

 code_size=read_char(fp);
 byte_count=read_char(fp);
 clear_code=1<<code_size;
 end_of_data=FALSE;
 bt=0;

 nbits=code_size+1;
 max_code=1<<nbits;
 free_code=FIRST;

 for(;;)  /* decompressor main loop */
   {
     cur_code=read_code();
     if(cur_code==END_CODE) return;
     if(cur_code==clear_code)
       {
	 nbits=code_size+1;
	 max_code=1<<nbits;
	 free_code=FIRST;
	 fin_char=k=old_code=cur_code=read_code();
	 put_pixel(k);
       }
     else
       {
	 in_code=cur_code;
	 if(cur_code>=free_code)
	   {
	     cur_code=old_code;
	     push(fin_char);
	   }
	 while(cur_code>=clear_code)
	   {
	     push(table[cur_code].ch);
	     cur_code=table[cur_code].next;
	   }
	 k=fin_char=cur_code;
	 push(k);

	  /* output pixels from stack in reverse order */
	 while(stackpointer>stack) put_pixel(pop());

	 table[free_code].ch=k;  /* add code */
	 table[free_code].next=old_code;
	 if(++free_code>=max_code)
	   {
	     if(nbits<MAXBITS)
	       {
		 nbits++;
		 max_code<<=1;
	       }
	   }
	 old_code=in_code;
       }
   }
}

/*
  reads one LZW-compression code from GIF-file
  note data block format and bit & byte order
 */
int read_code()
{
 register int code=0,bb,bl=nbits;

 static int masks[]={ 0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff };

 if(end_of_data) return END_CODE;

 while(bl)
  {
   if(bt==0)
    {
     ch=read_char(fp);
     if(ch==EOF) return END_CODE;
     byte_count--;
     if(byte_count==0)
      {
	byte_count=read_char(fp);
	if(byte_count==0 && byte_count==EOF)
	  {
	   end_of_data=TRUE;
	   break;
	  }
      }
     bt=8;
    }

   bb= bt < bl ? bt : bl; /* min(bt,bl) */
   code |= (((ch >> (8-bt))&masks[bb]) << (nbits-bl));
   bl-=bb;
   bt-=bb;
  }
 return code;
}

/*
  swaps the low and high bytes in a 16-bit word
  this is necessary because GIF uses lo-byte/hi-byte order.
 */
void flip_word(ptr)
UWORD *ptr;
{
 register char a,*c=(char *)ptr;

 a=c[0