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