1 #ident "@(#)gif.c 1.5 91/04/01 XGRASP"
2 /*-
3 * gif.c - routine to load GIF images (hacked from gif2ras).
4 *
5 * Copyright (c) 1991 by Patrick J. Naughton
6 *
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation for any purpose and without fee is hereby granted,
9 * provided that the above copyright notice appear in all copies and that
10 * both that copyright notice and this permission notice appear in
11 * supporting documentation.
12 *
13 * This file is provided AS IS with no warranties of any kind. The author
14 * shall have no liability with respect to the infringement of copyrights,
15 * trade secrets or any patents by this file or any part thereof. In no
16 * event will the author be liable for any lost revenue or profits or
17 * other special, indirect and consequential damages.
18 *
19 * Comments and additions should be sent to the author:
20 *
21 * Patrick J. Naughton
22 * Sun Microsystems
23 * 2550 Garcia Ave, MS 10-20
24 * Mountain View, CA 94043
25 * (415) 336-1080
26 *
27 */
28
29 #include "grasp.h"
30
31 #define NEXTBYTE (*ptr++)
32 #define IMAGESEP 0x2c
33 #define INTERLACEMASK 0x40
34 #define COLORMAPMASK 0x80
35
36 static int BitOffset, /* Bit Offset of next code */
37 XC, YC, /* Output X and Y coords of current pixel */
38 Pass, /* Used by output routine if interlaced pic */
39 OutCount, /* Decompressor output 'stack count' */
40 RWidth, RHeight, /* screen dimensions */
41 Width, Height, /* image dimensions */
42 LeftOfs, TopOfs, /* image offset */
43 BitsPerPixel, /* Bits per pixel, read from GIF header */
44 ColorMapSize, /* number of colors */
45 CodeSize, /* Code size, read from GIF header */
46 InitCodeSize, /* Starting code size, used during Clear */
47 LZWCode, /* Value returned by ReadCode */
48 MaxCode, /* limiting value for current code size */
49 ClearCode, /* GIF clear code */
50 EOFCode, /* GIF end-of-information code */
51 CurCode, OldCode, InCode, /* Decompressor variables */
52 FirstFree, /* First free code, generated per GIF spec */
53 FreeCode, /* Decompressor, next free slot in hash table */
54 FinChar, /* Decompressor variable */
55 BitMask, /* AND mask for data size */
56 ReadMask, /* Code AND mask for current code size */
57 Interlace, HasColormap;
58
59 static u_char *Image; /* The result array */
60 static u_char *RawGIF; /* The heap array to hold it, raw */
61 static u_char *Raster; /* The raster data stream, unblocked */
62
63 /* The hash table used by the decompressor */
64 static int Prefix[4096];
65 static int Suffix[4096];
66
67 /* An output array used by the decompressor */
68 static int OutCode[1025];
69
70 static char *id = "GIF87a";
71
72
73 /* Fetch the next code from the raster data stream. The codes can be
74 * any length from 3 to 12 bits, packed into 8-bit bytes, so we have to
75 * maintain our location in the Raster array as a BIT Offset. We compute
76 * the byte Offset into the raster array by dividing this by 8, pick up
77 * three bytes, compute the bit Offset into our 24-bit chunk, shift to
78 * bring the desired code to the bottom, then mask it off and return it.
79 */
80 static int
ReadCode()81 ReadCode()
82 {
83 int RawCode, ByteOffset;
84
85 ByteOffset = BitOffset / 8;
86 RawCode = Raster[ByteOffset] + (0x100 * Raster[ByteOffset + 1]);
87 if (CodeSize >= 8)
88 RawCode += (0x10000 * Raster[ByteOffset + 2]);
89 RawCode >>= (BitOffset % 8);
90 BitOffset += CodeSize;
91 return (RawCode & ReadMask);
92 }
93
94 static void
AddToPixel(Index)95 AddToPixel(Index)
96 u_char Index;
97 {
98 *(Image + YC * Width + XC) = Index;
99
100 /* Update the X-coordinate, and if it overflows, update the Y-coordinate */
101
102 if (++XC == Width) {
103
104 /* If a non-interlaced picture, just increment YC to the next scan line.
105 * If it's interlaced, deal with the interlace as described in the GIF
106 * spec. Put the decoded scan line out to the screen if we haven't gone
107 * past the bottom of it
108 */
109
110 XC = 0;
111 if (!Interlace)
112 YC++;
113 else {
114 switch (Pass) {
115 case 0:
116 YC += 8;
117 if (YC >= Height) {
118 Pass++;
119 YC = 4;
120 }
121 break;
122 case 1:
123 YC += 8;
124 if (YC >= Height) {
125 Pass++;
126 YC = 2;
127 }
128 break;
129 case 2:
130 YC += 4;
131 if (YC >= Height) {
132 Pass++;
133 YC = 1;
134 }
135 break;
136 case 3:
137 YC += 2;
138 break;
139 default:
140 break;
141 }
142 }
143 }
144 }
145
146
147 ImageStruct *
readgifimage(fp,dirent)148 readgifimage(fp, dirent)
149 FILE *fp;
150 FilenameStruct *dirent;
151 {
152 ImageStruct *im;
153 XImage *xim;
154 int filesize;
155 u_char ch, ch1;
156 u_char *ptr, *ptr1;
157 int i;
158
159 BitOffset = 0;
160 XC = 0;
161 YC = 0;
162 Pass = 0;
163 OutCount = 0;
164
165 im = (ImageStruct *) malloc(sizeof(ImageStruct));
166
167 fseek(fp, dirent->offset, 0);
168
169 filesize = GetLong(fp); /* length of whole image file... */
170
171 im->name = strtok(strdup(dirent->fname), ".");
172 im->type = EXT_GIF;
173 im->xoff = 0;
174 im->yoff = 0;
175
176 if (!(ptr = RawGIF = (u_char *) malloc(filesize)))
177 error("%s: not enough memory to read gif file.\n", NULL);
178
179 if (!(Raster = (u_char *) malloc(filesize)))
180 error("%s: not enough memory to read gif file.\n", NULL);
181
182 if (fread(ptr, filesize, 1, fp) != 1)
183 error("%s: GIF data read failed\n", NULL);
184
185 if (strncmp(ptr, id, 6)) {
186 free(im->name);
187 free(im);
188 free(Raster);
189 free(ptr);
190 return (ImageStruct *) readimage(fp, dirent, EXT_PIC);
191 }
192 ptr += 6;
193
194 /* Get variables from the GIF screen descriptor */
195
196 ch = NEXTBYTE;
197 RWidth = ch + 0x100 * NEXTBYTE; /* screen dimensions... not used. */
198 ch = NEXTBYTE;
199 RHeight = ch + 0x100 * NEXTBYTE;
200
201 ch = NEXTBYTE;
202 HasColormap = ((ch & COLORMAPMASK) ? True : False);
203
204 BitsPerPixel = (ch & 7) + 1;
205 im->cmaplen = 1 << BitsPerPixel;
206 BitMask = im->cmaplen - 1;
207
208 ch = NEXTBYTE; /* background color... not used. */
209
210 if (NEXTBYTE) /* supposed to be NULL */
211 error("%s: %s is a corrupt GIF file (nonull).\n", im->name);
212
213 /* Read in global colormap. */
214
215 if (HasColormap) {
216 unsigned long pmasks;
217 u_long pixels[256];
218
219 im->cmap = XCreateColormap(dsp, win, vis, AllocNone);
220 XAllocColorCells(dsp, im->cmap, True, &pmasks, 0, pixels, im->cmaplen);
221
222 for (i = 0; i < im->cmaplen; i++) {
223 im->colors[i].pixel = pixels[i];
224 im->colors[i].red = NEXTBYTE << 8;
225 im->colors[i].green = NEXTBYTE << 8;
226 im->colors[i].blue = NEXTBYTE << 8;
227 im->colors[i].flags = DoRed | DoGreen | DoBlue;
228
229 if (imverbose) {
230 printf("%02x%02x%02x ",
231 im->colors[i].red >> 8,
232 im->colors[i].green >> 8,
233 im->colors[i].blue >> 8);
234 if (!((i + 1) % 8))
235 printf("\n");
236 }
237 }
238 XStoreColors(dsp, im->cmap, im->colors, im->cmaplen);
239 } else
240 im->cmap = (Colormap) 0;
241
242 /* Check for image seperator */
243
244 if (NEXTBYTE != IMAGESEP)
245 error("%s: %s is a corrupt GIF file (nosep).\n", im->name);
246 /* Now read in values from the image descriptor */
247
248 ch = NEXTBYTE;
249 LeftOfs = ch + 0x100 * NEXTBYTE;
250 ch = NEXTBYTE;
251 TopOfs = ch + 0x100 * NEXTBYTE;
252 ch = NEXTBYTE;
253 Width = ch + 0x100 * NEXTBYTE;
254 ch = NEXTBYTE;
255 Height = ch + 0x100 * NEXTBYTE;
256 Interlace = ((NEXTBYTE & INTERLACEMASK) ? True : False);
257
258 if (verbose)
259 fprintf(stderr, "%s: (GIF) %dx%dx%d %s\n",
260 im->name, Width, Height, 8,
261 (Interlace) ? "Interlaced" : "");
262
263 /* Note that I ignore the possible existence of a local color map.
264 * I'm told there aren't many files around that use them, and the spec
265 * says it's defined for future use. This could lead to an error
266 * reading some files.
267 */
268
269 /* Start reading the raster data. First we get the intial code size
270 * and compute decompressor constant values, based on this code size.
271 */
272
273 CodeSize = NEXTBYTE;
274 ClearCode = (1 << CodeSize);
275 EOFCode = ClearCode + 1;
276 FreeCode = FirstFree = ClearCode + 2;
277
278 /* The GIF spec has it that the code size is the code size used to
279 * compute the above values is the code size given in the file, but the
280 * code size used in compression/decompression is the code size given in
281 * the file plus one. (thus the ++).
282 */
283
284 CodeSize++;
285 InitCodeSize = CodeSize;
286 MaxCode = (1 << CodeSize);
287 ReadMask = MaxCode - 1;
288
289 /* Read the raster data. Here we just transpose it from the GIF array
290 * to the Raster array, turning it from a series of blocks into one long
291 * data stream, which makes life much easier for ReadCode().
292 */
293
294 ptr1 = Raster;
295 do {
296 ch = ch1 = NEXTBYTE;
297 while (ch--)
298 *ptr1++ = NEXTBYTE;
299 if ((Raster - ptr1) > filesize)
300 error("%s: %s is a corrupt GIF file (unblock).\n", im->name);
301 } while (ch1);
302
303 free(RawGIF); /* We're done with the raw data now... */
304
305 Image = (u_char *) malloc(Width * Height);
306 if (!Image)
307 error("%s: malloc failed on image data.\n");
308
309 /* Decompress the file, continuing until you see the GIF EOF code.
310 * One obvious enhancement is to add checking for corrupt files here.
311 */
312
313 LZWCode = ReadCode();
314 while (LZWCode != EOFCode) {
315
316 /* Clear code sets everything back to its initial value, then reads the
317 * immediately subsequent code as uncompressed data.
318 */
319
320 if (LZWCode == ClearCode) {
321 CodeSize = InitCodeSize;
322 MaxCode = (1 << CodeSize);
323 ReadMask = MaxCode - 1;
324 FreeCode = FirstFree;
325 CurCode = OldCode = LZWCode = ReadCode();
326 FinChar = CurCode & BitMask;
327 AddToPixel(FinChar);
328 } else {
329
330 /* If not a clear code, then must be data: save same as CurCode and InCode */
331
332 CurCode = InCode = LZWCode;
333
334 /* If greater or equal to FreeCode, not in the hash table yet;
335 * repeat the last character decoded
336 */
337
338 if (CurCode >= FreeCode) {
339 CurCode = OldCode;
340 OutCode[OutCount++] = FinChar;
341 }
342 /* Unless this code is raw data, pursue the chain pointed to by CurCode
343 * through the hash table to its end; each code in the chain puts its
344 * associated output code on the output queue.
345 */
346
347 while (CurCode > BitMask) {
348 if (OutCount > 1024) {
349 fprintf(stderr, "%s is a corrupt GIF file (OutCount).\n",
350 im->name);
351 goto error_exit;
352 }
353 OutCode[OutCount++] = Suffix[CurCode];
354 CurCode = Prefix[CurCode];
355 }
356
357 /* The last code in the chain is treated as raw data. */
358
359 FinChar = CurCode & BitMask;
360 OutCode[OutCount++] = FinChar;
361
362 /* Now we put the data out to the Output routine.
363 * It's been stacked LIFO, so deal with it that way...
364 */
365
366 for (i = OutCount - 1; i >= 0; i--)
367 AddToPixel(OutCode[i]);
368 OutCount = 0;
369
370 /* Build the hash table on-the-fly. No table is stored in the file. */
371
372 Prefix[FreeCode] = OldCode;
373 Suffix[FreeCode] = FinChar;
374 OldCode = InCode;
375
376 /* Point to the next slot in the table. If we exceed the current
377 * MaxCode value, increment the code size unless it's already 12. If it
378 * is, do nothing: the next code decompressed better be CLEAR
379 */
380
381 FreeCode++;
382 if (FreeCode >= MaxCode) {
383 if (CodeSize < 12) {
384 CodeSize++;
385 MaxCode *= 2;
386 ReadMask = (1 << CodeSize) - 1;
387 }
388 }
389 }
390 LZWCode = ReadCode();
391 }
392 error_exit:
393
394 free(Raster);
395
396 im->w = Width;
397 im->h = Height;
398 im->d = 8;
399 xim = XCreateImage(dsp, vis, im->d, ZPixmap, 0, Image,
400 im->w, im->h, 8, im->w);
401 im->pix = XCreatePixmap(dsp, win, im->w, im->h, 8);
402 XPutImage(dsp, im->pix, gc, xim, 0, 0, 0, 0, im->w, im->h);
403 XSync(dsp, False);
404 free(Image);
405 return im;
406 }
407