#ident "@(#)gif.c 1.5 91/04/01 XGRASP" /*- * gif.c - routine to load GIF images (hacked from gif2ras). * * Copyright (c) 1991 by Patrick J. Naughton * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Comments and additions should be sent to the author: * * Patrick J. Naughton * Sun Microsystems * 2550 Garcia Ave, MS 10-20 * Mountain View, CA 94043 * (415) 336-1080 * */ #include "grasp.h" #define NEXTBYTE (*ptr++) #define IMAGESEP 0x2c #define INTERLACEMASK 0x40 #define COLORMAPMASK 0x80 static int BitOffset, /* Bit Offset of next code */ XC, YC, /* Output X and Y coords of current pixel */ Pass, /* Used by output routine if interlaced pic */ OutCount, /* Decompressor output 'stack count' */ RWidth, RHeight, /* screen dimensions */ Width, Height, /* image dimensions */ LeftOfs, TopOfs, /* image offset */ BitsPerPixel, /* Bits per pixel, read from GIF header */ ColorMapSize, /* number of colors */ CodeSize, /* Code size, read from GIF header */ InitCodeSize, /* Starting code size, used during Clear */ LZWCode, /* Value returned by ReadCode */ MaxCode, /* limiting value for current code size */ ClearCode, /* GIF clear code */ EOFCode, /* GIF end-of-information code */ CurCode, OldCode, InCode, /* Decompressor variables */ FirstFree, /* First free code, generated per GIF spec */ FreeCode, /* Decompressor, next free slot in hash table */ FinChar, /* Decompressor variable */ BitMask, /* AND mask for data size */ ReadMask, /* Code AND mask for current code size */ Interlace, HasColormap; static u_char *Image; /* The result array */ static u_char *RawGIF; /* The heap array to hold it, raw */ static u_char *Raster; /* The raster data stream, unblocked */ /* The hash table used by the decompressor */ static int Prefix[4096]; static int Suffix[4096]; /* An output array used by the decompressor */ static int OutCode[1025]; static char *id = "GIF87a"; /* Fetch the next code from the raster data stream. The codes can be * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to * maintain our location in the Raster array as a BIT Offset. We compute * the byte Offset into the raster array by dividing this by 8, pick up * three bytes, compute the bit Offset into our 24-bit chunk, shift to * bring the desired code to the bottom, then mask it off and return it. */ static int ReadCode() { int RawCode, ByteOffset; ByteOffset = BitOffset / 8; RawCode = Raster[ByteOffset] + (0x100 * Raster[ByteOffset + 1]); if (CodeSize >= 8) RawCode += (0x10000 * Raster[ByteOffset + 2]); RawCode >>= (BitOffset % 8); BitOffset += CodeSize; return (RawCode & ReadMask); } static void AddToPixel(Index) u_char Index; { *(Image + YC * Width + XC) = Index; /* Update the X-coordinate, and if it overflows, update the Y-coordinate */ if (++XC == Width) { /* If a non-interlaced picture, just increment YC to the next scan line. * If it's interlaced, deal with the interlace as described in the GIF * spec. Put the decoded scan line out to the screen if we haven't gone * past the bottom of it */ XC = 0; if (!Interlace) YC++; else { switch (Pass) { case 0: YC += 8; if (YC >= Height) { Pass++; YC = 4; } break; case 1: YC += 8; if (YC >= Height) { Pass++; YC = 2; } break; case 2: YC += 4; if (YC >= Height) { Pass++; YC = 1; } break; case 3: YC += 2; break; default: break; } } } } ImageStruct * readgifimage(fp, dirent) FILE *fp; FilenameStruct *dirent; { ImageStruct *im; XImage *xim; int filesize; u_char ch, ch1; u_char *ptr, *ptr1; int i; BitOffset = 0; XC = 0; YC = 0; Pass = 0; OutCount = 0; im = (ImageStruct *) malloc(sizeof(ImageStruct)); fseek(fp, dirent->offset, 0); filesize = GetLong(fp); /* length of whole image file... */ im->name = strtok(strdup(dirent->fname), "."); im->type = EXT_GIF; im->xoff = 0; im->yoff = 0; if (!(ptr = RawGIF = (u_char *) malloc(filesize))) error("%s: not enough memory to read gif file.\n", NULL); if (!(Raster = (u_char *) malloc(filesize))) error("%s: not enough memory to read gif file.\n", NULL); if (fread(ptr, filesize, 1, fp) != 1) error("%s: GIF data read failed\n", NULL); if (strncmp(ptr, id, 6)) { free(im->name); free(im); free(Raster); free(ptr); return (ImageStruct *) readimage(fp, dirent, EXT_PIC); } ptr += 6; /* Get variables from the GIF screen descriptor */ ch = NEXTBYTE; RWidth = ch + 0x100 * NEXTBYTE; /* screen dimensions... not used. */ ch = NEXTBYTE; RHeight = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; HasColormap = ((ch & COLORMAPMASK) ? True : False); BitsPerPixel = (ch & 7) + 1; im->cmaplen = 1 << BitsPerPixel; BitMask = im->cmaplen - 1; ch = NEXTBYTE; /* background color... not used. */ if (NEXTBYTE) /* supposed to be NULL */ error("%s: %s is a corrupt GIF file (nonull).\n", im->name); /* Read in global colormap. */ if (HasColormap) { unsigned long pmasks; u_long pixels[256]; im->cmap = XCreateColormap(dsp, win, vis, AllocNone); XAllocColorCells(dsp, im->cmap, True, &pmasks, 0, pixels, im->cmaplen); for (i = 0; i < im->cmaplen; i++) { im->colors[i].pixel = pixels[i]; im->colors[i].red = NEXTBYTE << 8; im->colors[i].green = NEXTBYTE << 8; im->colors[i].blue = NEXTBYTE << 8; im->colors[i].flags = DoRed | DoGreen | DoBlue; if (imverbose) { printf("%02x%02x%02x ", im->colors[i].red >> 8, im->colors[i].green >> 8, im->colors[i].blue >> 8); if (!((i + 1) % 8)) printf("\n"); } } XStoreColors(dsp, im->cmap, im->colors, im->cmaplen); } else im->cmap = (Colormap) 0; /* Check for image seperator */ if (NEXTBYTE != IMAGESEP) error("%s: %s is a corrupt GIF file (nosep).\n", im->name); /* Now read in values from the image descriptor */ ch = NEXTBYTE; LeftOfs = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; TopOfs = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; Width = ch + 0x100 * NEXTBYTE; ch = NEXTBYTE; Height = ch + 0x100 * NEXTBYTE; Interlace = ((NEXTBYTE & INTERLACEMASK) ? True : False); if (verbose) fprintf(stderr, "%s: (GIF) %dx%dx%d %s\n", im->name, Width, Height, 8, (Interlace) ? "Interlaced" : ""); /* Note that I ignore the possible existence of a local color map. * I'm told there aren't many files around that use them, and the spec * says it's defined for future use. This could lead to an error * reading some files. */ /* Start reading the raster data. First we get the intial code size * and compute decompressor constant values, based on this code size. */ CodeSize = NEXTBYTE; ClearCode = (1 << CodeSize); EOFCode = ClearCode + 1; FreeCode = FirstFree = ClearCode + 2; /* The GIF spec has it that the code size is the code size used to * compute the above values is the code size given in the file, but the * code size used in compression/decompression is the code size given in * the file plus one. (thus the ++). */ CodeSize++; InitCodeSize = CodeSize; MaxCode = (1 << CodeSize); ReadMask = MaxCode - 1; /* Read the raster data. Here we just transpose it from the GIF array * to the Raster array, turning it from a series of blocks into one long * data stream, which makes life much easier for ReadCode(). */ ptr1 = Raster; do { ch = ch1 = NEXTBYTE; while (ch--) *ptr1++ = NEXTBYTE; if ((Raster - ptr1) > filesize) error("%s: %s is a corrupt GIF file (unblock).\n", im->name); } while (ch1); free(RawGIF); /* We're done with the raw data now... */ Image = (u_char *) malloc(Width * Height); if (!Image) error("%s: malloc failed on image data.\n"); /* Decompress the file, continuing until you see the GIF EOF code. * One obvious enhancement is to add checking for corrupt files here. */ LZWCode = ReadCode(); while (LZWCode != EOFCode) { /* Clear code sets everything back to its initial value, then reads the * immediately subsequent code as uncompressed data. */ if (LZWCode == ClearCode) { CodeSize = InitCodeSize; MaxCode = (1 << CodeSize); ReadMask = MaxCode - 1; FreeCode = FirstFree; CurCode = OldCode = LZWCode = ReadCode(); FinChar = CurCode & BitMask; AddToPixel(FinChar); } else { /* If not a clear code, then must be data: save same as CurCode and InCode */ CurCode = InCode = LZWCode; /* If greater or equal to FreeCode, not in the hash table yet; * repeat the last character decoded */ if (CurCode >= FreeCode) { CurCode = OldCode; OutCode[OutCount++] = FinChar; } /* Unless this code is raw data, pursue the chain pointed to by CurCode * through the hash table to its end; each code in the chain puts its * associated output code on the output queue. */ while (CurCode > BitMask) { if (OutCount > 1024) { fprintf(stderr, "%s is a corrupt GIF file (OutCount).\n", im->name); goto error_exit; } OutCode[OutCount++] = Suffix[CurCode]; CurCode = Prefix[CurCode]; } /* The last code in the chain is treated as raw data. */ FinChar = CurCode & BitMask; OutCode[OutCount++] = FinChar; /* Now we put the data out to the Output routine. * It's been stacked LIFO, so deal with it that way... */ for (i = OutCount - 1; i >= 0; i--) AddToPixel(OutCode[i]); OutCount = 0; /* Build the hash table on-the-fly. No table is stored in the file. */ Prefix[FreeCode] = OldCode; Suffix[FreeCode] = FinChar; OldCode = InCode; /* Point to the next slot in the table. If we exceed the current * MaxCode value, increment the code size unless it's already 12. If it * is, do nothing: the next code decompressed better be CLEAR */ FreeCode++; if (FreeCode >= MaxCode) { if (CodeSize < 12) { CodeSize++; MaxCode *= 2; ReadMask = (1 << CodeSize) - 1; } } } LZWCode = ReadCode(); } error_exit: free(Raster); im->w = Width; im->h = Height; im->d = 8; xim = XCreateImage(dsp, vis, im->d, ZPixmap, 0, Image, im->w, im->h, 8, im->w); im->pix = XCreatePixmap(dsp, win, im->w, im->h, 8); XPutImage(dsp, im->pix, gc, xim, 0, 0, 0, 0, im->w, im->h); XSync(dsp, False); free(Image); return im; }