SXWRR@ALASKA.BITNET (Reed Rector) (05/13/89)
[GIF Image Decompression] As promised weeks ago, here is some 'C' source code that will read and decompress a GIF image file. (This program converts it to a Sun Raster image, but it is no great problem converting for use in other programs) Any problems or questions, just let me know -Reed SXWRR@ALASKA (Bitnet) SXWRR@acad3.fai.alaska.edu (internet) --- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # # gif2ras.c # # This archive created: Fri May 12 20:22:24 1989 # By: Roger L. Long (bytebug@dhw68k.cts.com) export PATH; PATH=/bin:$PATH echo shar: extracting "'gif2ras.c'" '(14611 characters)' if test -f 'gif2ras.c' then echo shar: will not over-write existing file "'gif2ras.c'" else sed 's/^X//' << \SHAR_EOF > 'gif2ras.c' X/* X * gif2ras.c - Converts from a Compuserve GIF (tm) image to a Sun Raster image. X * X * Copyright (c) 1988 by Patrick J. Naughton X * X * Author: Patrick J. Naughton X * naughton@wind.sun.com X * X * Permission to use, copy, modify, and distribute this software and its X * documentation for any purpose and without fee is hereby granted, X * provided that the above copyright notice appear in all copies and that X * both that copyright notice and this permission notice appear in X * supporting documentation. X * X * This file is provided AS IS with no warranties of any kind. The author X * shall have no liability with respect to the infringement of copyrights, X * trade secrets or any patents by this file or any part thereof. In no X * event will the author be liable for any lost revenue or profits or X * other special, indirect and consequential damages. X * X * Comments and additions should be sent to the author: X * X * Patrick J. Naughton X * Sun Microsystems, Inc. X * 2550 Garcia Ave, MS 14-40 X * Mountain View, CA 94043 X * (415) 336-1080 X * X * Revision History: X * 28-Aug-88: Modified by Jef Poskanzer to output PBM instead of Sun raster. X * 27-Jul-88: Updated to use libpixrect to fix 386i byteswapping problems. X * 11-Apr-88: Converted to C and changed to write Sun rasterfiles. X * 19-Jan-88: GIFSLOW.PAS posted to comp.graphics by Jim Briebel, X * a Turbo Pascal 4.0 program to painfully slowly display X * GIF images on an EGA equipped IBM-PC. X * X * Description: X * This program takes a Compuserve "Graphics Interchange Format" or "GIF" X * file as input and writes a file known as a Sun rasterfile. This datafile X * can be loaded by the NeWS "readcanvas" operator and is of the same format X * as the files in /usr/NeWS/smi/*. Under X11R2 there is a program called X * xraster to display these files. X * X * Portability: X * To make this program convert to some image format other than PBM X * format simply seach for the tag "PBMS:" in the source and X * replace these simple mechanisms with the appropriate ones for the X * other output format. I have marked all (six) PBM Specific pieces X * of code with this comment. X * X * SS: compile with "cc -o gif2ras -O gif2ras.c -lpixrect" X * PBMS: compile with "cc -o giftopbm -O giftopbm.c libpbm.a X */ X X#include <stdio.h> X#ifdef notdefSS X#include <pixrect/pixrect_hs.h> /* SS: main Pixrect header file */ X#endif notdefSS X#include <sys/types.h> X#include "pbm.h" /* PBMS: main PBM header file */ X Xtypedef int boolean; X#define True (1) X#define False (0) X X#define NEXTSHORT (*ptr++ + (0x100 * *ptr++)) X#define NEXTBYTE (*ptr++) X#define IMAGESEP 0x2c X#define INTERLACEMASK 0x40 X#define COLORMAPMASK 0x80 X Xint BitOffset = 0, /* Bit Offset of next code */ X XC = 0, YC = 0, /* Output X and Y coords of current pixel */ X Pass = 0, /* Used by output routine if interlaced pic */ X OutCount = 0, /* Decompressor output 'stack count' */ X RWidth, RHeight, /* screen dimensions */ X Width, Height, /* image dimensions */ X LeftOfs, TopOfs, /* image offset */ X BitsPerPixel, /* Bits per pixel, read from GIF header */ X ColorMapSize, /* number of colors */ X CodeSize, /* Code size, read from GIF header */ X InitCodeSize, /* Starting code size, used during Clear */ X Code, /* Value returned by ReadCode */ X MaxCode, /* limiting value for current code size */ X ClearCode, /* GIF clear code */ X EOFCode, /* GIF end-of-information code */ X CurCode, OldCode, InCode, /* Decompressor variables */ X FirstFree, /* First free code, generated per GIF spec */ X FreeCode, /* Decompressor, next free slot in hash table */ X FinChar, /* Decompressor variable */ X BitMask, /* AND mask for data size */ X ReadMask; /* Code AND mask for current code size */ X Xboolean Interlace, HasColormap; X X#ifdef notdefSS X/* SS: defined in pixrect/pixrect_hs.h */ XPixrect *Output; /* The Sun Pixrect */ Xcolormap_t Colormap; /* The Pixrect Colormap */ Xu_char *Image; /* The result array */ X#endif notdefSS X/* PBMS: defined in pbm.h */ Xbit **bits; /* The PBM bit array */ X Xu_char *RawGIF; /* The heap array to hold it, raw */ Xu_char *Raster; /* The raster data stream, unblocked */ X X /* The hash table used by the decompressor */ Xint Prefix[4096]; Xint Suffix[4096]; X X /* An output array used by the decompressor */ Xint OutCode[1025]; X X /* The color map, read from the GIF header */ Xu_char Red[256], Green[256], Blue[256]; X Xchar *id = "GIF87a"; X Xchar *pname; /* program name (used for error messages) */ X Xvoid Xerror(s1, s2) Xchar *s1, *s2; X{ X fprintf(stderr, s1, pname, s2); X exit(1); X} X X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ XFILE *fp; Xchar *infname = argv[1]; Xchar *outfname = argv[2]; Xint filesize; Xregister u_char ch, ch1; Xregister u_char *ptr, *ptr1; Xregister int i; X X setbuf(stderr, NULL); X pname = argv[0]; X X if (argc < 3) X error("usage: %s GIFfile rasterfile\n", NULL); X X if (!(fp = fopen(infname, "r"))) X error("%s: %s not found.\n", infname); X X /* find the size of the file */ X X fseek(fp, 0L, 2); X filesize = ftell(fp); X fseek(fp, 0L, 0); X X if (!(ptr = RawGIF = (u_char *) malloc(filesize))) X error("%s: not enough memory to read gif file.\n", NULL); X X if (!(Raster = (u_char *) malloc(filesize))) X error("%s: not enough memory to read gif file.\n", NULL); X X fread(ptr, filesize, 1, fp); X X if (strncmp(ptr, id, 6)) X error("%s: %s is not a GIF file.\n", infname); X ptr += 6; X X/* Get variables from the GIF screen descriptor */ X X RWidth = NEXTSHORT; /* screen dimensions... not used. */ X RHeight = NEXTSHORT; X X ch = NEXTBYTE; X HasColormap = ((ch & COLORMAPMASK) ? True : False); X X BitsPerPixel = (ch & 7) + 1; X ColorMapSize = 1 << BitsPerPixel; X BitMask = ColorMapSize - 1; X X ch = NEXTBYTE; /* background color... not used. */ X X if (NEXTBYTE) /* supposed to be NULL */ X error("%s: %s is a corrupt GIF file.\n", infname); X X/* Read in global colormap. */ X X if (HasColormap) { X fprintf(stderr, "%s is %d bits per pixel, (%d colors).\n", X infname, BitsPerPixel, ColorMapSize); X for (i = 0; i < ColorMapSize; i++) { X Red[i] = NEXTBYTE; X Green[i] = NEXTBYTE; X Blue[i] = NEXTBYTE; X } X X#ifdef notdefSS X/* SS: Fill in the Pixrect colormap struct */ X Colormap.type = RMT_EQUAL_RGB; X Colormap.length = ColorMapSize; X Colormap.map[0] = Red; X Colormap.map[1] = Green; X Colormap.map[2] = Blue; X#endif notdefSS X /* PBMS: PBM only handles bitmaps. Reject any pixmaps here. */ X if (BitsPerPixel != 1) X error("%s: %s has more than one bit per pixel - it's not a bitmap.\n X", infname); X } X else error("%s: %s does not have a colormap.\n", infname); X X X/* Check for image seperator */ X X if (NEXTBYTE != IMAGESEP) X error("%s: %s is a corrupt GIF file.\n", infname); X X/* Now read in values from the image descriptor */ X X LeftOfs = NEXTSHORT; X TopOfs = NEXTSHORT; X Width = NEXTSHORT; X Height = NEXTSHORT; X Interlace = ((NEXTBYTE & INTERLACEMASK) ? True : False); X X fprintf(stderr, "Reading a %d by %d %sinterlaced image...", X Width, Height, (Interlace) ? "" : "non-"); X X X/* Note that I ignore the possible existence of a local color map. X * I'm told there aren't many files around that use them, and the spec X * says it's defined for future use. This could lead to an error X * reading some files. X */ X X/* Start reading the raster data. First we get the intial code size X * and compute decompressor constant values, based on this code size. X */ X X CodeSize = NEXTBYTE; X ClearCode = (1 << CodeSize); X EOFCode = ClearCode + 1; X FreeCode = FirstFree = ClearCode + 2; X X/* The GIF spec has it that the code size is the code size used to X * compute the above values is the code size given in the file, but the X * code size used in compression/decompression is the code size given in X * the file plus one. (thus the ++). X */ X X CodeSize++; X InitCodeSize = CodeSize; X MaxCode = (1 << CodeSize); X ReadMask = MaxCode - 1; X X/* Read the raster data. Here we just transpose it from the GIF array X * to the Raster array, turning it from a series of blocks into one long X * data stream, which makes life much easier for ReadCode(). X */ X X ptr1 = Raster; X do { X ch = ch1 = NEXTBYTE; X while (ch--) *ptr1++ = NEXTBYTE; X } while(ch1); X X free(RawGIF); /* We're done with the raw data now... */ X X fprintf(stderr, "done.\n"); X fprintf(stderr, "Decompressing..."); X X X#ifdef notdefSS X/* SS: Allocate the Sun Pixrect and make "Image" point to the image data. */ X Output = mem_create(Width, Height, 8); X if (Output == (Pixrect *) NULL) X error("%s: not enough memory for output data.\n", NULL); X Image = (u_char *) mpr_d(Output)->md_image; X#endif notdefSS X/* PBMS: Allocate the PBM bit array. */ X bits = pbm_allocarray(Width, Height); X X X/* Decompress the file, continuing until you see the GIF EOF code. X * One obvious enhancement is to add checking for corrupt files here. X */ X X Code = ReadCode(); X while (Code != EOFCode) { X X/* Clear code sets everything back to its initial value, then reads the X * immediately subsequent code as uncompressed data. X */ X X if (Code == ClearCode) { X CodeSize = InitCodeSize; X MaxCode = (1 << CodeSize); X ReadMask = MaxCode - 1; X FreeCode = FirstFree; X CurCode = OldCode = Code = ReadCode(); X FinChar = CurCode & BitMask; X AddToPixel(FinChar); X } X else { X X/* If not a clear code, then must be data: save same as CurCode and InCode */ X X CurCode = InCode = Code; X X/* If greater or equal to FreeCode, not in the hash table yet; X * repeat the last character decoded X */ X X if (CurCode >= FreeCode) { X CurCode = OldCode; X OutCode[OutCount++] = FinChar; X } X X/* Unless this code is raw data, pursue the chain pointed to by CurCode X * through the hash table to its end; each code in the chain puts its X * associated output code on the output queue. X */ X X while (CurCode > BitMask) { X OutCode[OutCount++] = Suffix[CurCode]; X CurCode = Prefix[CurCode]; X } X X/* The last code in the chain is treated as raw data. */ X X FinChar = CurCode & BitMask; X OutCode[OutCount++] = FinChar; X X/* Now we put the data out to the Output routine. X * It's been stacked LIFO, so deal with it that way... X */ X X for (i = OutCount - 1; i >= 0; i--) X AddToPixel(OutCode[i]); X OutCount = 0; X X/* Build the hash table on-the-fly. No table is stored in the file. */ X X Prefix[FreeCode] = OldCode; X Suffix[FreeCode] = FinChar; X OldCode = InCode; X X/* Point to the next slot in the table. If we exceed the current X * MaxCode value, increment the code size unless it's already 12. If it X * is, do nothing: the next code decompressed better be CLEAR X */ X X FreeCode++; X if (FreeCode >= MaxCode) { X if (CodeSize < 12) { X CodeSize++; X MaxCode *= 2; X ReadMask = (1 << CodeSize) - 1; X } X } X } X Code = ReadCode(); X } X X free(Raster); X X fprintf(stderr, "done.\n"); X X if (!(fp = fopen(outfname, "w"))) X error("%s: can't create %s.\n", outfname); X X#ifdef notdefSS X/* SS: Pixrect Rasterfile output code. */ X fprintf(stderr, "Writing Sun Rasterfile: %s...", outfname); X if (pr_dump(Output, fp, &Colormap, RT_STANDARD, 0) == PIX_ERR) X error("%s: error writing Sun Rasterfile: %s\n", outfname); X#endif notdefSS X/* PBMS: PBM output code. */ X pbm_writepbm(stdout, bits, Width, Height); X X fclose(fp); X X fprintf(stderr, "done.\n"); X} X X X/* Fetch the next code from the raster data stream. The codes can be X * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to X * maintain our location in the Raster array as a BIT Offset. We compute X * the byte Offset into the raster array by dividing this by 8, pick up X * three bytes, compute the bit Offset into our 24-bit chunk, shift to X * bring the desired code to the bottom, then mask it off and return it. X */ XReadCode() X{ Xint RawCode, ByteOffset; X X ByteOffset = BitOffset / 8; X RawCode = Raster[ByteOffset] + (0x100 * Raster[ByteOffset + 1]); X if (CodeSize >= 8) X RawCode += (0x10000 * Raster[ByteOffset + 2]); X RawCode >>= (BitOffset % 8); X BitOffset += CodeSize; X return(RawCode & ReadMask); X} X X XAddToPixel(Index) Xu_char Index; X{ X#ifdef notdefSS X *(Image + YC * Width + XC) = Index; X#endif notdefSS X/* PBMS: Store a pixel. */ X bits[YC][XC] = Index; X X/* Update the X-coordinate, and if it overflows, update the Y-coordinate */ X X if (++XC == Width) { X X/* If a non-interlaced picture, just increment YC to the next scan line. X * If it's interlaced, deal with the interlace as described in the GIF X * spec. Put the decoded scan line out to the screen if we haven't gone X * past the bottom of it X */ X X XC = 0; X if (!Interlace) YC++; X else { X switch (Pass) { X case 0: X YC += 8; X if (YC >= Height) { X Pass++; X YC = 4; X } X break; X case 1: X YC += 8; X if (YC >= Height) { X Pass++; X YC = 2; X } X break; X case 2: X YC += 4; X if (YC >= Height) { X Pass++; X YC = 1; X } X break; X case 3: X YC += 2; X break; X default: X break; X } X } X } X} SHAR_EOF if test 14611 -ne "`wc -c < 'gif2ras.c'`" then echo shar: error transmitting "'gif2ras.c'" '(should have been 14611 characters)' fi fi # end of overwriting check # End of shell archive exit 0 ---