rnd-19980904
[rocksndiamonds.git] / src / gif.c
1
2 /* gif.c:
3  *
4  * adapted from code by kirk johnson (tuna@athena.mit.edu).  most of this
5  * code is unchanged. -- jim frost 12.31.89
6  *
7  * gifin.c
8  * kirk johnson
9  * november 1989
10  *
11  * routines for reading GIF files
12  *
13  * Copyright 1989 Kirk L. Johnson (see the included file
14  * "kljcpyrght.h" for complete copyright information)
15  */
16
17 #define GIF_C
18
19 #include "xli.h"
20 #include "gif.h"
21
22 #define PUSH_PIXEL(p)                                       \
23 {                                                           \
24   if (pstk_idx == PSTK_SIZE)                                \
25     return GIFIN_ERR_PSO;                                   \
26   else                                                      \
27     pstk[pstk_idx++] = (p);                                 \
28 }
29
30 /*
31  * push a string (denoted by a code) onto the pixel stack
32  * (returns the code of the first pixel in the string in ps_rslt)
33  */
34
35 int ps_rslt;    /* return result */
36
37 #define GIFIN_PUSH_STRING(code)                             \
38 {                                                           \
39   int ps_code;                                              \
40   ps_code = code;                                           \
41                                                             \
42   while (((unsigned int)ps_code) < STAB_SIZE && prefix[ps_code] != NULL_CODE) \
43   {                                                         \
44     PUSH_PIXEL(extnsn[ps_code]);                            \
45     ps_code = prefix[ps_code];                              \
46   }                                                         \
47                                                             \
48   if (((unsigned int)ps_code) >= STAB_SIZE)                                 \
49     return GIFIN_ERR_TAO;                                   \
50   PUSH_PIXEL(extnsn[ps_code]);                              \
51   ps_rslt = extnsn[ps_code];                                \
52   if (((unsigned int)ps_rslt) >= STAB_SIZE)                                 \
53     return GIFIN_ERR_TAO;                                   \
54 }
55
56 /*
57  * Look up the ascii message coresponding to
58  * the error number.
59  */
60 static char *get_err_string(int errno)
61 {
62   int i;
63   for (i=0;gif_err_strings[i].err_no != 0;i++)
64   {
65     if (gif_err_strings[i].err_no == errno)
66                 return gif_err_strings[i].name;
67   }
68   return "";
69 }
70
71
72 static int interlace_start[4] = /* start line for interlacing */
73 {
74   0, 4, 2, 1
75 };
76
77 static int interlace_rate[4] =  /* rate at which we accelerate vertically */
78 {
79   8, 8, 4, 2
80 };
81
82 static BYTE file_open  = 0;     /* status flags */
83 static BYTE image_open = 0;
84
85 static FILE *ins;              /* input stream */
86
87 static int  root_size;          /* root code size */
88 static int  clr_code;           /* clear code */
89 static int  eoi_code;           /* end of information code */
90 static int  code_size;          /* current code size */
91 static int  code_mask;          /* current code mask */
92 static int  prev_code;          /* previous code */
93
94 static long work_data;          /* working bit buffer */
95 static int  work_bits;          /* working bit count */
96
97 static BYTE buf[256];           /* byte buffer */
98 static int  buf_cnt;            /* byte count */
99 static int  buf_idx;            /* buffer index */
100
101 static int table_size;          /* string table size */
102 static int prefix[STAB_SIZE];   /* string table : prefixes */
103 static int extnsn[STAB_SIZE];   /* string table : extensions */
104
105 static BYTE pstk[PSTK_SIZE];    /* pixel stack */
106 static int  pstk_idx;           /* pixel stack pointer */
107
108
109 static int  gifin_rast_width;          /* raster width */
110 static int  gifin_rast_height;         /* raster height */
111 static BYTE gifin_g_cmap_flag;         /* global colormap flag */
112 static int  gifin_g_pixel_bits;        /* bits per pixel, global colormap */
113 static int  gifin_g_ncolors;           /* number of colors, global colormap */
114 static BYTE gifin_g_cmap[3][256];      /* global colormap */
115 static BYTE gifin_g_cmap_sorted;       /* global colormap sorted (GIF89a only) */
116 static int  gifin_bg_color;            /* background color index */
117 static int  gifin_color_bits;          /* bits of color resolution */
118 static double  gifin_aspect;           /* pixel aspect ratio (width/height) */
119 static int  gifin_version;             /* gif file version */
120
121 static int  gifin_img_left;            /* image position on raster */
122 static int  gifin_img_top;             /* image position on raster */
123 static int  gifin_img_width;           /* image width */
124 static int  gifin_img_height;          /* image height */
125 static BYTE gifin_l_cmap_flag;         /* local colormap flag */
126 static int  gifin_l_pixel_bits;        /* bits per pixel, local colormap */
127 static int  gifin_l_ncolors;           /* number of colors, local colormap */
128 static BYTE gifin_l_cmap[3][256];      /* local colormap */
129 static BYTE gifin_interlace_flag;      /* interlace image format flag */
130
131 /*
132  * open a GIF file, using s as the input stream
133  */
134
135 static int gifin_open_file(FILE *s)
136 {
137   int errno;
138   /* make sure there isn't already a file open */
139   if (file_open)
140     return GIFIN_ERR_FAO;
141
142   /* remember that we've got this file open */
143   file_open = 1;
144   ins       = s;
145
146   /* check GIF signature */
147   if (fread(buf, 1, GIF_SIG_LEN, ins) != GIF_SIG_LEN)
148     return GIFIN_ERR_EOF;
149
150   buf[GIF_SIG_LEN] = '\0';
151   if (strcmp((char *) buf, GIF_SIG) == 0)
152     gifin_version = GIF87a;
153   else if(strcmp((char *) buf, GIF_SIG_89) == 0)
154     gifin_version = GIF89a;
155   else
156     return GIFIN_ERR_BAD_SIG;
157
158   /* read screen descriptor */
159   if (fread(buf, 1, GIF_SD_SIZE, ins) != GIF_SD_SIZE)
160     return GIFIN_ERR_EOF;
161
162   /* decode screen descriptor */
163   gifin_rast_width   = (buf[1] << 8) + buf[0];
164   gifin_rast_height  = (buf[3] << 8) + buf[2];
165   gifin_g_cmap_flag  = (buf[4] & 0x80) ? 1 : 0;
166   gifin_color_bits   = (((int)(buf[4] & 0x70)) >> 4) + 1;
167   gifin_g_pixel_bits = (buf[4] & 0x07) + 1;
168   gifin_bg_color     = buf[5];
169   gifin_aspect = 1.0;
170
171   if (gifin_version == GIF87a)
172   {
173     if (buf[4] & 0x08 || buf[6] != 0)
174       return GIFIN_ERR_BAD_SD;
175   }
176   else
177   {
178     gifin_g_cmap_sorted = ((buf[4] & 0x08) != 0);
179     if (buf[6] != 0)
180       gifin_aspect = ((double)buf[6] + 15.0) / 64.0;
181   }
182
183   /* load global colormap */
184   if (gifin_g_cmap_flag)
185   {
186     gifin_g_ncolors = (1 << gifin_g_pixel_bits);
187
188     if ((errno = gifin_load_cmap(gifin_g_cmap, gifin_g_ncolors)) != GIFIN_SUCCESS)
189       return errno;
190   }
191   else
192   {
193     gifin_g_ncolors = 0;
194   }
195
196   /* done! */
197   return GIFIN_SUCCESS;
198 }
199
200
201 /*
202  * open next GIF image in the input stream; returns GIFIN_SUCCESS if
203  * successful. if there are no more images, returns GIFIN_DONE. (might
204  * also return various GIFIN_ERR codes.)
205  */
206
207 static int gifin_open_image()
208 {
209   int i;
210   int separator;
211   int errno;
212
213   /* make sure there's a file open */
214   if (!file_open)
215     return GIFIN_ERR_NFO;
216
217   /* make sure there isn't already an image open */
218   if (image_open)
219     return GIFIN_ERR_IAO;
220
221   /* remember that we've got this image open */
222   image_open = 1;
223
224   /* skip over any extension blocks */
225   do
226   {
227     separator = fgetc(ins);
228     if (separator == GIF_EXTENSION)
229     {
230       if ((errno = gifin_skip_extension()) != GIFIN_SUCCESS)
231         return errno;
232     }
233   }
234   while (separator == GIF_EXTENSION);
235
236   /* check for end of file marker */
237   if (separator == GIF_TERMINATOR)
238     return GIFIN_DONE;
239
240   /* make sure we've got an image separator */
241   if (separator != GIF_SEPARATOR)
242     return GIFIN_ERR_BAD_SEP;
243
244   /* read image descriptor */
245   if (fread(buf, 1, GIF_ID_SIZE, ins) != GIF_ID_SIZE)
246     return GIFIN_ERR_EOF;
247
248   /* decode image descriptor */
249   gifin_img_left       = (buf[1] << 8) + buf[0];
250   gifin_img_top        = (buf[3] << 8) + buf[2];
251   gifin_img_width      = (buf[5] << 8) + buf[4];
252   gifin_img_height     = (buf[7] << 8) + buf[6];
253   gifin_l_cmap_flag    = (buf[8] & 0x80) ? 1 : 0;
254   gifin_interlace_flag = (buf[8] & 0x40) ? 1 : 0;
255   gifin_l_pixel_bits   = (buf[8] & 0x07) + 1;
256
257   /* load local colormap */
258   if (gifin_l_cmap_flag)
259   {
260     gifin_l_ncolors = (1 << gifin_l_pixel_bits);
261
262     if ((errno = gifin_load_cmap(gifin_l_cmap, gifin_l_ncolors)) != GIFIN_SUCCESS)
263       return errno;
264   }
265   else
266   {
267     gifin_l_ncolors = 0;
268   }
269
270   /* initialize raster data stream decoder */
271   root_size = fgetc(ins);
272   clr_code  = 1 << root_size;
273   eoi_code  = clr_code + 1;
274   code_size = root_size + 1;
275   code_mask = (1 << code_size) - 1;
276   work_bits = 0;
277   work_data = 0;
278   buf_cnt   = 0;
279   buf_idx   = 0;
280
281   /* initialize string table */
282   for (i=0; i<STAB_SIZE; i++)
283   {
284     prefix[i] = NULL_CODE;
285     extnsn[i] = i;
286   }
287
288   /* initialize pixel stack */
289   pstk_idx = 0;
290
291   /* done! */
292   return GIFIN_SUCCESS;
293 }
294
295 /*
296  * try to read next pixel from the raster, return result in *pel
297  */
298
299 static int gifin_get_pixel(pel)
300      int *pel;
301 {
302   int  code;
303   int  first;
304   int  place;
305   int  errno;
306
307   /* decode until there are some pixels on the pixel stack */
308   while (pstk_idx == 0)
309   {
310     /* load bytes until we have enough bits for another code */
311     while (work_bits < code_size)
312     {
313       if (buf_idx == buf_cnt)
314       {
315         /* read a new data block */
316         if ((errno = gifin_read_data_block()) != GIFIN_SUCCESS)
317           return errno;
318
319         if (buf_cnt == 0)
320           return GIFIN_ERR_EOD;
321       }
322
323       work_data |= ((long) buf[buf_idx++]) << work_bits;
324       work_bits += 8;
325     }
326
327     /* get the next code */
328     code        = work_data & code_mask;
329     work_data >>= code_size;
330     work_bits  -= code_size;
331
332     /* interpret the code */
333     if (code == clr_code)
334     {
335       /* reset decoder stream */
336       code_size  = root_size + 1;
337       code_mask  = (1 << code_size) - 1;
338       prev_code  = NULL_CODE;
339       table_size = eoi_code + 1;
340     }
341     else if (code == eoi_code)
342     {
343       /* Ooops! no more pixels */
344       return GIFIN_ERR_EOF;
345     }
346     else if (prev_code == NULL_CODE)
347     {
348       GIFIN_PUSH_STRING(code);
349       prev_code = code;
350     }
351     else
352     {
353       if (code < table_size)
354       {
355         GIFIN_PUSH_STRING(code);
356         first = ps_rslt;
357       }
358       else
359       {
360         place = pstk_idx;
361         PUSH_PIXEL(NULL_CODE);
362         GIFIN_PUSH_STRING(prev_code);
363         first = ps_rslt;
364         pstk[place] = first;
365       }
366
367       if((errno = gifin_add_string(prev_code, first)) != GIFIN_SUCCESS)
368         return errno;
369       prev_code = code;
370     }
371   }
372
373   /* pop a pixel off the pixel stack */
374   *pel = (int) pstk[--pstk_idx];
375
376   /* done! */
377   return GIFIN_SUCCESS;
378 }
379
380 /*
381  * close an open GIF file
382  */
383
384 static int gifin_close_file()
385 {
386   /* make sure there's a file open */
387   if (!file_open)
388     return GIFIN_ERR_NFO;
389
390   /* mark file (and image) as closed */
391   file_open  = 0;
392   image_open = 0;
393
394   /* done! */
395   return GIFIN_SUCCESS;
396 }
397
398 /*
399  * load a colormap from the input stream
400  */
401
402 static int gifin_load_cmap(BYTE cmap[3][256], int ncolors)
403 {
404   int i;
405
406   for (i=0; i<ncolors; i++)
407   {
408     if (fread(buf, 1, 3, ins) != 3)
409       return GIFIN_ERR_EOF;
410     
411     cmap[GIF_RED][i] = buf[GIF_RED];
412     cmap[GIF_GRN][i] = buf[GIF_GRN];
413     cmap[GIF_BLU][i] = buf[GIF_BLU];
414   }
415
416   /* done! */
417   return GIFIN_SUCCESS;
418 }
419  
420 /*
421  * skip an extension block in the input stream
422  */
423
424 static int gifin_skip_extension()
425 {
426   int errno;
427
428   /* get the extension function byte */
429   fgetc(ins);
430
431   /* skip any remaining raster data */
432   do
433   {
434     if ((errno = gifin_read_data_block()) != GIFIN_SUCCESS)
435       return errno;
436   }
437   while (buf_cnt > 0);
438
439   /* done! */
440   return GIFIN_SUCCESS;
441 }
442
443 /*
444  * read a new data block from the input stream
445  */
446
447 static int gifin_read_data_block()
448 {
449   /* read the data block header */
450   buf_cnt = fgetc(ins);
451
452   /* read the data block body */
453   if (fread(buf, 1, buf_cnt, ins) != buf_cnt)
454     return GIFIN_ERR_EOF;
455
456   buf_idx = 0;
457
458   /* done! */
459   return GIFIN_SUCCESS;
460 }
461
462
463 /*
464  * add a new string to the string table
465  */
466
467 static int gifin_add_string(int p, int e)
468 {
469   prefix[table_size] = p;
470   extnsn[table_size] = e;
471
472   if ((table_size == code_mask) && (code_size < 12))
473   {
474     code_size += 1;
475     code_mask  = (1 << code_size) - 1;
476   }
477
478   table_size += 1;
479   if (table_size > STAB_SIZE)
480     return GIFIN_ERR_TAO;
481   return GIFIN_SUCCESS;
482 }
483
484 /* these are the routines added for interfacing to xli
485  */
486
487 /* tell someone what the image we're loading is.  this could be a little more
488  * descriptive but I don't care
489  */
490
491 static void tellAboutImage(char *name)
492 {
493   printf("\n%s:\n  %dx%d %s%s image with %d colors\n",
494          name, gifin_img_width, gifin_img_height,
495          (gifin_interlace_flag ? "interlaced " : ""),
496          gif_version_name[gifin_version],
497          (gifin_l_cmap_flag ? gifin_l_ncolors : gifin_g_ncolors));
498 }
499
500 Image *gifLoad(char *fullname)
501 {
502   FILE *zf;
503   Image *image;
504   int x, y, pixel, pass, scanlen;
505   byte *pixptr, *pixline;
506   int errno;
507
508   if (!(zf = fopen(fullname,"r")))
509   {
510     perror("gifLoad");
511     return(NULL);
512   }
513
514   if ((gifin_open_file(zf) != GIFIN_SUCCESS) || /* read GIF header */
515       (gifin_open_image() != GIFIN_SUCCESS))    /* read image header */
516   {
517     printf("gifin_open_file or gifin_open_image failed!\n");
518
519     gifin_close_file();
520     fclose(zf);
521     return(NULL);
522   }
523
524   /*
525   tellAboutImage(fullname);
526   */
527
528   image= newRGBImage(gifin_img_width, gifin_img_height, (gifin_l_cmap_flag ?
529                                                          gifin_l_pixel_bits :
530                                                          gifin_g_pixel_bits));
531   image->title= dupString(fullname);
532
533   /* if image has a local colormap, override global colormap
534    */
535
536   if (gifin_l_cmap_flag)
537   {
538     for (x= 0; x < gifin_l_ncolors; x++)
539     {
540       image->rgb.red[x]= gifin_l_cmap[GIF_RED][x] << 8;
541       image->rgb.green[x]= gifin_l_cmap[GIF_GRN][x] << 8;
542       image->rgb.blue[x]= gifin_l_cmap[GIF_BLU][x] << 8;
543     }
544     image->rgb.used= gifin_l_ncolors;
545   }
546   else
547   {
548     for (x= 0; x < gifin_g_ncolors; x++)
549     {
550       image->rgb.red[x]= gifin_g_cmap[GIF_RED][x] << 8;
551       image->rgb.green[x]= gifin_g_cmap[GIF_GRN][x] << 8;
552       image->rgb.blue[x]= gifin_g_cmap[GIF_BLU][x] << 8;
553     }
554     image->rgb.used= gifin_g_ncolors;
555   }
556
557   /* interlaced image -- futz with the vertical trace.  i wish i knew what
558    * kind of drugs the GIF people were on when they decided that they
559    * needed to support interlacing.
560    */
561
562   if (gifin_interlace_flag)
563   {
564     scanlen= image->height * image->pixlen;
565
566     /* interlacing takes four passes to read, each starting at a different
567      * vertical point.
568      */
569
570     for (pass= 0; pass < 4; pass++)
571     {
572       y= interlace_start[pass];
573       scanlen= image->width * image->pixlen * interlace_rate[pass];
574       pixline= image->data + (y * image->width * image->pixlen);
575       while (y < gifin_img_height)
576       {
577         pixptr= pixline;
578         for (x= 0; x < gifin_img_width; x++)
579         {
580           if ((errno = gifin_get_pixel(&pixel)) != GIFIN_SUCCESS)
581           {
582             fprintf(stderr, "gifLoad: %s - Short read within image data, '%s'\n", fullname, get_err_string(errno));
583             y = gifin_img_height; x = gifin_img_width;
584           }
585           valToMem(pixel, pixptr, image->pixlen);
586           pixptr += image->pixlen;
587         }
588         y += interlace_rate[pass];
589         pixline += scanlen;
590       }
591     }
592   }
593   else
594   {
595     /* not an interlaced image, just read in sequentially
596      */
597
598     if(image->pixlen == 1)      /* the usual case */
599     {
600       pixptr= image->data;
601       for (y= 0; y < gifin_img_height; y++)
602         for (x= 0; x < gifin_img_width; x++)
603         {
604           if ((errno = gifin_get_pixel(&pixel)) != GIFIN_SUCCESS)
605           {
606             fprintf(stderr, "gifLoad: %s - Short read within image data, '%s'\n", fullname, get_err_string(errno));
607             y = gifin_img_height; x = gifin_img_width;
608           }
609           valToMem(pixel, pixptr, 1);
610           pixptr += 1;
611         }
612     }
613     else                        /* less ususal case */
614     {
615       pixptr= image->data;
616       for (y= 0; y < gifin_img_height; y++)
617         for (x= 0; x < gifin_img_width; x++)
618         {
619           if ((errno = gifin_get_pixel(&pixel)) != GIFIN_SUCCESS)
620           {
621             fprintf(stderr, "gifLoad: %s - Short read within image data, '%s'\n", fullname, get_err_string(errno));
622             y = gifin_img_height; x = gifin_img_width;
623           }
624           valToMem(pixel, pixptr, image->pixlen);
625           pixptr += image->pixlen;
626         }
627      }
628   }
629
630   gifin_close_file();
631   fclose(zf);
632
633   return(image);
634 }