[comp.lang.postscript] giftops C-source program

tang@pv7017.vincent.iastate.edu (Tang De-Ming) (12/06/90)

I got this program quite awhile ago.  It does not work on MSDOS machine 
even though the author claims that it is very portable.  Someone may want
to patch it to fix the memory problem.  So far, it has worked for
me on a SGI Iris machine.  This program has two parts:
1. giftops.h
2. giftops.c
I have combined those two files and labelled them by "cut here..."

I did not write this program and no question should be directed to me.
The author stated before that it is placed in public domain and thus you
may distribute it freely to your friends.

Deming Tang
Iowa State University

--------------Cut here and save into a file called giftops.h-----------
/* local types */
#define min(x,y) ((x) < (y) ? (x) : (y))
#define FALSE 0
#define TRUE 1

typedef int bool;
typedef struct codestruct {
            struct codestruct *prefix;
            unsigned char first,suffix;
        } codetype;

/* local prototypes */
void usage(void);
void fatal(char *s);
void checksignature(void);
void initcolors(char colortable[256][3],
                unsigned char colormap[256][3],
                int ncolors);
void writeheader(unsigned int left, unsigned int top,
                 unsigned int width, unsigned int height);
void outcode(codetype *p,unsigned char **fill);
void process(int code,unsigned char **fill);
void readscreen(void);
void rasterize(int row, int width);
void readraster(unsigned int width,unsigned int height);
void writetrailer(void);
void readimage(void);
void readextension(void);
------------------------------End of giftops.h---------------------------

------------Cut here to save below into a file called giftops.c---------
/*********************************************
 *             GIFtoPS Converter             *
 *                                           *
 *      May 16, 1988  by Scott Hemphill      *
 *                                           *
 * I wrote this program, and hereby place it *
 * in the public domain, i.e. there are no   *
 * copying restrictions of any kind.         *
 *********************************************/

#include <stdio.h>
#include "giftops.h"

char *malloc();
int strncmp();

FILE *infile;
unsigned int screenwidth;           /* The dimensions of the screen */
unsigned int screenheight;          /*   (not those of the image)   */
bool global;                        /* Is there a global color map? */
int globalbits;                     /* Number of bits of global colors */
unsigned char globalmap[256][3];    /* RGB values for global color map */
char colortable[256][3];            /* Hex intensity strings for an image */
unsigned char *raster;              /* Decoded image data */
codetype *codetable;                /* LZW compression code data */
int datasize,codesize,codemask;     /* Decoder working variables */
int clear,eoi;                      /* Special code values */

void usage(void)
{
        fprintf(stderr,"usage: giftops input-file > output-file\n");
        exit(-1);
}

void fatal(char *s)
{
        fprintf(stderr,"giftops: %s\n",s);
        exit(-1);
}

void checksignature(void)
{
        char buf[6];

        fread(buf,1,6,infile);
        if (strncmp(buf,"GIF",3)) fatal("file is not a GIF file");
        if (strncmp(&buf[3],"87a",3)) fatal("unknown GIF version number");
}

/* Get information which is global to all the images stored in the file */

void readscreen(void)
{
        unsigned char buf[7];

        fread(buf,1,7,infile);
        screenwidth = buf[0] + (buf[1] << 8);
        screenheight = buf[2] + (buf[3] << 8);
        global = buf[4] & 0x80;
        if (global) {
            globalbits = (buf[4] & 0x07) + 1;
            fread(globalmap,3,1<<globalbits,infile);
        }
}

/* Convert a color map (local or global) to an array of two character
   hexadecimal strings, stored in colortable.  RGB is converted to
   8-bit grayscale using integer arithmetic. */

void initcolors(char colortable[256][3],
                unsigned char colormap[256][3],
                int ncolors)
{
        static char hextab[] = {'0','1','2','3','4','5','6','7',
                                '8','9','A','B','C','D','E','F'};
        register unsigned int color;
        register int i;

        for (i = 0; i < ncolors; i++) {
            color = 77*colormap[i][0] + 150*colormap[i][1] + 29*colormap[i][2];
            color >>= 8;
            colortable[i][0] = hextab[color >> 4];
            colortable[i][1] = hextab[color & 15];
            colortable[i][2] = '\0';
        }
}

/* Write a postscript header to the standard output.  Standard paper size
   (8.5 by 11) is hard-coded, as is the whole initialization sequence. */

void writeheader(unsigned int left,unsigned int top,
                 unsigned int width,unsigned int height)
{
        double scale;
        int scaledwidth,scaledheight;

        scale = min(648.0/screenwidth, 468.0/screenheight);
        scaledwidth = (int)(scale*screenwidth+0.5);
        scaledheight = (int)(scale*screenheight+0.5);

        printf("currentscreen /proc exch def /angle exch def /frequency exch def\n");
        printf("/angle 90 def /frequency 60 def\n");
        printf("frequency angle /proc load setscreen\n");
        printf("/picstr %d string def\n",width);
        printf("/screen {%d %d 8 [%d 0 0 -%d 0 %d]\n",width,height,width,height,height);
        printf("   {currentfile picstr readhexstring pop} image} def\n");
        printf("%d %d translate 90 rotate %d %d scale screen\n",
               306+(scaledheight>>1),396-(scaledwidth>>1),
               scaledwidth, scaledheight);
}

/* Output the bytes associated with a code to the raster array */

void outcode(register codetype *p,register unsigned char **fill)
{
        if (p->prefix) outcode(p->prefix,fill);
        *(*fill)++ = p->suffix;
}

/* Process a compression code.  "clear" resets the code table. Otherwise
   make a new code table entry, and output the bytes associated with the
   code. */

void process(register int code,unsigned char **fill)
{
        static avail,oldcode;
        register codetype *p;

        if (code == clear) {
            codesize = datasize + 1;
            codemask = (1 << codesize) - 1;
            avail = clear + 2;
            oldcode = -1;
        } else if (code < avail) {
            outcode(&codetable[code],fill);
            if (oldcode != -1) {
                p = &codetable[avail++];
                p->prefix = &codetable[oldcode];
                p->first = p->prefix->first;
                p->suffix = codetable[code].first;
                if ((avail & codemask) == 0 && avail < 4096) {
                    codesize++;
                    codemask += avail;
                }
            }
            oldcode = code;
        } else if (code == avail && oldcode != -1) {
            p = &codetable[avail++];
            p->prefix = &codetable[oldcode];
            p->first = p->prefix->first;
            p->suffix = p->first;
            outcode(p,fill);
            if ((avail & codemask) == 0 && avail < 4096) {
                codesize++;
                codemask += avail;
            }
            oldcode = code;
        } else {
            fatal("illegal code in raster data");
        }
}

/* Decode a raster image */

void readraster(unsigned int width,unsigned int height)
{
        unsigned char *fill = raster;
        unsigned char buf[255];
        register int bits=0;
        register unsigned int count,datum=0;
        register unsigned char *ch;
        register int code;

        datasize = getc(infile);
        clear = 1 << datasize;
        eoi = clear+1;
        codesize = datasize + 1;
        codemask = (1 << codesize) - 1;
        codetable = (codetype*)malloc(4096*sizeof(codetype));
        if (!codetable) fatal("not enough memory for code table");
        for (code = 0; code < clear; code++) {
            codetable[code].prefix = (codetype*)0;
            codetable[code].first = code;
            codetable[code].suffix = code;
        }
        for (count = getc(infile); count > 0; count = getc(infile)) {
            fread(buf,1,count,infile);
            for (ch=buf; count-- > 0; ch++) {
                datum += *ch << bits;
                bits += 8;
                while (bits >= codesize) {
                    code = datum & codemask;
                    datum >>= codesize;
                    bits -= codesize;
                    if (code == eoi) goto exitloop;  /* This kludge put in
                                                        because some GIF files
                                                        aren't standard */
                    process(code,&fill);
                }
            }
        }
exitloop:
        if (fill != raster + width*height) fatal("raster has the wrong size");
        free(codetable);
}

/* Read a row out of the raster image and write it to the output file */

void rasterize(int row,int width)
{
        register unsigned char *scanline;
        register int i;

        scanline = raster + row*width;
        for (i = 0; i < width; i++) {
            if (i % 40 == 0) printf("\n");  /* break line every 80 chars */
            fputs(colortable[*scanline++],stdout);
        }
        printf("\n");
}

/* write image trailer to standard output */

void writetrailer(void)
{
        printf("showpage\n");
}

/* Read image information (position, size, local color map, etc.) and convert
   to postscript. */

void readimage(void)
{
        unsigned char buf[9];
        unsigned int left,top,width,height;
        bool local,interleaved;
        unsigned char localmap[256][3];
        int localbits;
        int *interleavetable;
        register int row;
        register int i;

        fread(buf,1,9,infile);
        left = buf[0] + (buf[1] << 8);
        top = buf[2] + (buf[3] << 8);
        width = buf[4] + (buf[5] << 8);
        height = buf[6] + (buf[7] << 8);
        local = buf[8] & 0x80;
        interleaved = buf[8] & 0x40;
        if (local) {
            localbits = (buf[8] & 0x7) + 1;
            fread(localmap,3,1<<localbits,infile);
            initcolors(colortable,localmap,1<<localbits);
        } else if (global) {
            initcolors(colortable,globalmap,1<<globalbits);
        } else {
            fatal("no colormap present for image");
        }
        writeheader(left,top,width,height);
        raster = (unsigned char*)malloc(width*height);
        if (!raster) fatal("not enough memory for image");
        readraster(width,height);
        if (interleaved) {
            interleavetable = (int*)malloc(height*sizeof(int));
            if (!interleavetable) fatal("not enough memory for interleave table");
            row = 0;
            for (i = top; i < top+height; i += 8) interleavetable[i] = row++;
            for (i = top+4; i < top+height; i += 8) interleavetable[i] = row++;
            for (i = top+2; i < top+height; i += 4) interleavetable[i] = row++;
            for (i = top+1; i < top+height; i += 2) interleavetable[i] = row++;
            for (row = top; row < top+height; row++)
rasterize(interleavetable[row],width);
            free(interleavetable);
        } else {
            for (row = top; row < top+height; row++) rasterize(row,width);
        }
        free(raster);
        writetrailer();
}

/* Read a GIF extension block (and do nothing with it). */

void readextension(void)
{
        unsigned char code;
        int count;
        char buf[255];

        code = getc(infile);
        while (count = getc(infile)) fread(buf,1,(long) count,infile);
}

void main(int argc,char *argv[])
{
        int quit = FALSE;
        char ch;

        if (argc != 2) usage();
        infile = fopen(argv[1],"r");
        if (!infile) {
            perror("giftops");
            exit(-1);
        }
        checksignature();
        readscreen();
        do {
            ch = getc(infile);
            switch (ch) {
                case '\0':  break;  /* this kludge for non-standard files */
                case ',':   readimage();
                            break;
                case ';':   quit = TRUE;
                            break;
                case '!':   readextension();
                            break;
                default:    fatal("illegal GIF block type");
                            break;
            }
        } while (!quit);
}
---------------------------End of giftops.c-------------------------------