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