[comp.sources.wanted] GIF file information reader

jdm@hodge.UUCP (jdm) (05/04/89)

There have been a few questions on the format of GIF files
lately (including many from myself).  I found this source file
on a BBS that examines the information present in a GIF image file
and gives a report.  It compiles under both Turbo C and MS C.

--------------------------cut here---------------------------------

/* GIFSCAN - scans through a GIF file and reports all parameters */

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

void	colormap(int out, FILE *dev, int times);
unsigned int getbytes(FILE *dev);
void	extninfo(FILE *dev);
void	chkunexp(int *unexpected, int determiner);
void	imageinf(FILE *dev, int bits_to_use, int color_style);

#define MAX 255


/************************************************************************/
/* MAIN - the main routine reads the GIF file for the global GIF        */
/*        information which it displays to the user's screen.  This     */
/*        routine also determines which subroutine to call and which    */
/*        information to process.                                       */


#define DEC "-d"
#define HEX "-h"
#define PCT "-p"
#define DECIMAL 1
#define PERCENT 2
#define HEXIDEC 3


main(int argc, char *argv[])
{
    char filename[15];
    char version[7];
    char style[5];

    int byte1;
    int byte2;
    int byte3;
    int color_res;
    int unexpected;
    int image_cnt;
    int bits_per_pix;
    int bits_to_use;
    int colors;
    int i;
    int globl;
    int end_gif_fnd;
    int color_style;
    int switch_present;

    unsigned int width;
    unsigned int height;

    FILE *in;

    /* Start of Processing */

    /* If just one argument then display the message */

    if (argc == 1) {
	printf("\nUSAGE:    gifscan  color_opt  filename\n");
	printf("\ncolor_opt   specifies how color lookup table");
	printf("\n            definitions are displayed");
	printf("\n  -h  :     output as hexidecimal (0 - FF) (default)");
	printf("\n  -d  :     output as decimal (0 - 255)");
	printf("\n  -p  :     output as percentage (0 - 100)\n");
	return;
    }

    color_style = 0;
    switch_present = 0;

    if ((strnicmp(argv[1], "-", 1) == 0)) {
	switch_present = 1;
	strcpy(style, argv[1]);
    }
    else
	switch_present = 0;

    do {
	if ((strnicmp(style, DEC, 2) == 0))
	    color_style = DECIMAL;
	else if ((strnicmp(style, PCT, 2) == 0))
	    color_style = PERCENT;
	else
	    color_style = HEXIDEC;
#if 0
	else if ((strnicmp(style, HEX, 2) == 0))
	    color_style = HEXIDEC;
	else {
	    printf("\nEnter Colormap style (-h, -p, -d) : ");
	    gets (style);
	}
#endif
    }
    while (color_style == 0)
	;

	
    /* Check for GIF filename */

    do {
	if ((argc == 1) || ((argc == 2) && (switch_present == 1))) {
	    printf("\nEnter GIF filename:  ");
	    gets (filename);
	}
	else
	    if (argc == 3)
		strcpy(filename, argv[2]);
	    else
		strcpy(filename, argv[1]);

	in = fopen (filename, "rb");
	argc = 1;
    }
    while (in == NULL)
	;

    image_cnt = 0;
    end_gif_fnd = 0;
    unexpected = 0;

    /* get version from file */

    if ((version[0] = getc(in)) == 0x47) {
	for (i = 1; (i < 6); i++)
	    version[i] = getc(in);
	version[6] = '\0';
	printf("\nVersion: %s", version);
    }
    else {
	printf("\n? -- NOT a GIF file\n");
	exit(1);
    }


    /* determine screen width */

    width = getbytes(in);

    /* determine screen height */

    height = getbytes(in);
    printf("\nScreen Width:     %5d\tScreen Height:    %5d", width, height);

    /* check for a Global Map */

    byte1 = getc(in);
    byte2 = byte1 & 0x80;
    if (byte2 == 0x80) {
	printf("\nGlobal Color Map: Yes");
	globl = 1;
    }
    else {
	printf("\nGlobal Color Map: No");
	globl = 0;
    }

    /* Check for the 0 bit */

    byte2 = byte1 & 0x08;
    if (byte2 != 0)
	printf("\n? -- Reserved zero bit is not zero.\n");

    /* determine the color resolution */

    byte2 = byte1 & 0x70;
    color_res = byte2 >> 4;


    /* get the background index */

    byte3 = getc(in);
    printf("\nColor res:  %5d\tBackground index:  %5d", ++color_res, byte3);

    /* determine the bits per pixel */

    bits_per_pix = byte1 & 0x07;
    bits_per_pix++;
    bits_to_use = bits_per_pix;

    /* determine # of colors in global map */

    colors = 1 << bits_per_pix;
    printf("\nBits per pixel: %5d\t# colors:  %5d\n", bits_per_pix, colors);


    /* check for the 0 byte */

    byte1 = getc(in);
    if (byte1 != 0)
	printf("\n? -- Reserved byte after Background index is not zero.\n");

    if (globl == 1)
	colormap (color_style, in, colors);

    /* check for the zero byte count, a new image, or */
    /* the end marker for the gif file */

    while ((byte1 = getc(in)) != EOF) {
	if (byte1 == ',') {
	    image_cnt++;
	    if (unexpected != 0)
		chkunexp(&unexpected, image_cnt);
	    printf("\nImage # %2d", image_cnt);
	    imageinf(in, bits_to_use, color_style);
	}
	else if (byte1 == '!')
	    extninfo (in);

	else if (byte1 == ';') {
	    if (unexpected != 0)
		chkunexp(&unexpected, -1);
	    end_gif_fnd = 1;
	}
	else
	    unexpected++;

    }

    /* EOF has been reached - check last bytes read */

    if (end_gif_fnd == 0)
	printf("\n? -- GIF file terminator ';' was not found.\n");
    else
	if (unexpected != 0)
	    chkunexp(&unexpected, -2);
}



/************************************************************************/
/* COLORMAP - reads color information in from the GIF file and displays */
/*            it in a user selected method.  This display may be in :   */
/*            hexidecimal (default), percentage, or decimal.  User      */
/*            selects output method by placing a switch (-d, -p, -h)    */
/*            between the program name and GIF filename at request time.*/
void
colormap(int out, FILE *dev, int times)
{
    unsigned int red;
    unsigned int green;
    unsigned int blue;

    int print_cnt;
    int i;

    /* Start of procedure */

    if (out == DECIMAL)
	printf("\nColor definitions in decimal   (index #, R, G, B)\n");

    if (out == PERCENT)
	printf("\nColor definitions by percentage   (index #, R, G, B)\n");

    if (out == HEXIDEC)
	printf("\nColor definitions in hexidecimal    (index #, R, G, B)\n");

    /* read and print the color definitions */

    print_cnt = 0;

    for (i = 0; (i < times); i++) {
	red = getc(dev);
	green = getc(dev);
	blue = getc(dev);

	switch (out) {
	case DECIMAL :
	    printf("%3d - %3d %3d %3d  ", i, red, green, blue);
	    break;
	case PERCENT :
	    red = (red * 100) / MAX;
	    green = (green * 100) / MAX;
	    blue = (blue * 100) / MAX;
	    printf("%3d - %3d %3d %3d  ", i, red, green, blue);
	    break;
	case HEXIDEC :
	    printf("%3d - %2x %2x %2x   ", i, red, green, blue);
	    break;
	}
	print_cnt++;
	if (print_cnt == 4) {
	    printf("\n");
	    print_cnt = 0;
	}
    }
    if ((times % 4) != 0)
	printf("\n");
}

/************************************************************************/

/* GETBYTES - routine to retrieve two bytes of information from the GIF */
/*            file and then shift them into correct byte order.  The    */
/*            information is stored in Least Significant Byte order.    */

unsigned int getbytes(FILE *dev)
{
    int byte1;
    int byte2;
    int result;

    /* read bytes and shift over */

    byte1 = getc(dev);
    byte2 = getc(dev);

    result = (byte2 << 8) | byte1;

    return result;

}


/***********************************************************************/
/* IMAGEINF - routine to read the GIF image information and display it */
/*            to the user's screen in an orderly fasion.  If there are */
/*            multiple images then IMAGEINF will be called to display  */
/*            multiple screens.                                        */

void
imageinf(FILE *dev, int bits_to_use, int color_style)
{
    int byte1;
    int byte2;
    int image_left;
    int image_top;
    int data_byte_cnt;
    int bits_per_pix;
    int colors;
    int i;
    int local;

    unsigned int width;
    unsigned int height;

    unsigned long bytetot;
    unsigned long possbytes;

    /* determine the image left value */

    image_left = getbytes(dev);

    /* determine the image top value */

    image_top = getbytes(dev);
    printf("\nImage Left: %5d\t\tImage Top: %5d", image_left, image_top);

    /* determine the image width */

    width = getbytes(dev);

    /* determine the image height */

    height = getbytes(dev);
    printf("\nImage Width:    %5d\t\tImage Height:  %5d", width, height);

    /* check for interlaced image */

    byte1 = getc(dev);
    byte2 = byte1 & 0x40;
    if (byte2 == 0x40)
	printf("\nInterlaced: Yes");
    else
	printf("\nInterlaced: No");

    /* check for a local map */

    byte2 = byte1 & 0x80;
    if (byte2 == 0x80) {
	local = 1;
	printf("\nLocal Color Map: Yes");
    }
    else {
	local = 0;
	printf("\nLocal Color Map: No");
    }

    /* check for the 3 zero bits */

    byte2 = byte1 & 0x38;
    if (byte2 != 0)
	printf("\n? -- Reserved zero bits in image not zeros.\n");

    /* determine the # of color bits in local map */

    bits_per_pix = byte1 & 0x07;
    bits_per_pix++;
    colors = 1 << bits_per_pix;

    if (local == 1) {
	bits_to_use = bits_per_pix;
	printf("\nBits per pixel: %5d\t\t# colors :     %5d", bits_per_pix,
		                                               colors);
	colormap (color_style, dev, colors);
    }

    /* retrieve the code size */

    byte1 = getc(dev);
    if ((byte1 < 2) || (byte1 > 8)) {
	printf("\n? -- Code size %d at start of image");
	printf("\n     is out of range (2-8).\n");
    }
    else
	printf("\nLZW min code size (bits):  %3d", byte1);

    /* tally up the total bytes and read past each data block */

    bytetot = 0;
    possbytes = 0;

    while ((data_byte_cnt = getc(dev)) > 0) {
	bytetot = bytetot + data_byte_cnt;
	for (i = 0; (i < data_byte_cnt); i++) {
	    byte2 = getc(dev);
	    if (byte2 == EOF) {
		printf("\n? -- EOF reached inside image data block.\n");
		exit (2);
	    }
	}
    }
    possbytes = (unsigned long) width * height;
    i = 8 / bits_to_use;
    possbytes = possbytes / i;
    printf("\nTotal bytes:  %ld out of possible  %ld\n", bytetot, possbytes);

    if (data_byte_cnt == EOF) {
	printf("\n? -- EOF reached before zero byte count");
	printf("\n     of image was read.\n");
	exit (3);
    }
}



/************************************************************************/
/* EXTNINFO - routine to read the GIF file for extension data and       */
/*            display it to the screen in an orderly fasion.  This      */
/*            extension information may be located before, between, or  */
/*            after any of the image data.                              */

void
extninfo(FILE *dev)
{
    int byte1;
    int byte2;
    int i;
    int data_byte_cnt;

    unsigned long bytetot;

    /* retrieve the function code */

    byte1 = getc(dev);
    printf("\nGIF extension seen, code :  %d", byte1);

    /* tally up the total bytes and read past each data block */

    bytetot = 0;

    while ((data_byte_cnt = getc(dev)) > 0) {
	bytetot = bytetot + data_byte_cnt;
	for (i = 0; (i < data_byte_cnt); i++) {
	    byte2 = getc(dev);
	    if (byte2 == EOF) {
		printf("\n? -- EOF reached inside extension data block.\n");
		exit (2);
	    }
	}
    }

    printf("\nTotal number of bytes in extension:  %ld\n", bytetot);

    if (data_byte_cnt == EOF) {
	printf("\n? -- EOF was reached before zero byte count");
	printf("\n     of extension was read.\n");
	exit (3);
    }
}


/************************************************************************/
/* CHKUNEXP - routine to check for any unexpected nonzero data found    */
/*            within the GIF file.  This routine will help determine    */
/*            where the unexpected data may reside in the file.         */

void
chkunexp (int *unexpected, int determiner)
{
    /* Determine place in the GIF file */

    if (determiner > 0) {
	printf("\n? -- %d bytes of unexpected data found before",*unexpected);
	printf("\n     image %d.\n", determiner);
    }
    else if (determiner == -1) {
	printf("\n? -- %d bytes of unexpected data found before",
		                                      *unexpected);
	printf("\n     GIF file terminator.\n");
    }
    else {
	printf("\n? -- %d bytes of unexpected data found after",
		                                      *unexpected);
	printf("\n     GIF file terminator.\n");
    }

    /* Zero out unexpected variable for next group that may be encountered */

    *unexpected = 0;
}

-- 

	jdm@hodge.cts.com [uunet zardoz vdelta crash]!hodge!jdm

	James D. Murray, Ethnounixologist
	Hodge Computer Research Corporation
	1588 North Batavia Street 
	Orange, California 92667  USA

	TEL: (714) 998-7750	Ask for James
	FAX: (714) 921-8038	Wait for the carrier