OSDN Git Service

fix #40957
[jnethack/source.git] / win / share / gifread.c
1 /* GIF reading routines based on those in pbmplus:ppm/giftoppm.c, bearing
2  * following copyright notice:
3  */
4
5 /* +-------------------------------------------------------------------+ */
6 /* | Copyright 1990, David Koblas.                                     | */
7 /* |   Permission to use, copy, modify, and distribute this software   | */
8 /* |   and its documentation for any purpose and without fee is hereby | */
9 /* |   granted, provided that the above copyright notice appear in all | */
10 /* |   copies and that both that copyright notice and this permission  | */
11 /* |   notice appear in supporting documentation.  This software is    | */
12 /* |   provided "as is" without express or implied warranty.           | */
13 /* +-------------------------------------------------------------------+ */
14
15 /*
16  * $NHDT-Date: 1432512803 2015/05/25 00:13:23 $  $NHDT-Branch: master $:$NHDT-Revision: 1.5 $
17  */
18
19 #include "config.h"
20 #include "tile.h"
21
22 #ifndef MONITOR_HEAP
23 extern long *FDECL(alloc, (unsigned int));
24 #endif
25
26 #define PPM_ASSIGN(p, red, grn, blu) \
27     do {                             \
28         (p).r = (red);               \
29         (p).g = (grn);               \
30         (p).b = (blu);               \
31     } while (0)
32
33 #define MAX_LWZ_BITS 12
34
35 #define INTERLACE 0x40
36 #define LOCALCOLORMAP 0x80
37 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
38
39 #define ReadOK(file, buffer, len) \
40     (fread((genericptr_t) buffer, (int) len, 1, file) != 0)
41
42 #define LM_to_uint(a, b) (((b) << 8) | (a))
43
44 struct gifscreen {
45     int Width;
46     int Height;
47     int Colors;
48     int ColorResolution;
49     int Background;
50     int AspectRatio;
51     int Interlace;
52 } GifScreen;
53
54 struct {
55     int transparent;
56     int delayTime;
57     int inputFlag;
58     int disposal;
59 } Gif89 = { -1, -1, -1, 0 };
60
61 int ZeroDataBlock = FALSE;
62
63 static FILE *gif_file;
64 static int tiles_across, tiles_down, curr_tiles_across, curr_tiles_down;
65 static pixel **image;
66 static unsigned char input_code_size;
67
68 static int FDECL(GetDataBlock, (FILE * fd, unsigned char *buf));
69 static void FDECL(DoExtension, (FILE * fd, int label));
70 static boolean FDECL(ReadColorMap, (FILE * fd, int number));
71 static void FDECL(read_header, (FILE * fd));
72 static int FDECL(GetCode, (FILE * fd, int code_size, int flag));
73 static int FDECL(LWZReadByte, (FILE * fd, int flag, int input_code_size));
74 static void FDECL(ReadInterleavedImage, (FILE * fd, int len, int height));
75 static void FDECL(ReadTileStrip, (FILE * fd, int len));
76
77 /* These should be in gif.h, but there isn't one. */
78 boolean FDECL(fopen_gif_file, (const char *, const char *));
79 boolean FDECL(read_gif_tile, (pixel(*) [TILE_X]));
80 int NDECL(fclose_gif_file);
81
82 static int
83 GetDataBlock(fd, buf)
84 FILE *fd;
85 unsigned char *buf;
86 {
87     unsigned char count;
88
89     if (!ReadOK(fd, &count, 1)) {
90         Fprintf(stderr, "error in getting DataBlock size\n");
91         return -1;
92     }
93
94     ZeroDataBlock = (count == 0);
95
96     if ((count != 0) && (!ReadOK(fd, buf, count))) {
97         Fprintf(stderr, "error in reading DataBlock\n");
98         return -1;
99     }
100
101     return count;
102 }
103
104 static void
105 DoExtension(fd, label)
106 FILE *fd;
107 int label;
108 {
109     static char buf[256];
110     char *str;
111
112     switch (label) {
113     case 0x01: /* Plain Text Extension */
114         str = "Plain Text Extension";
115 #ifdef notdef
116         if (GetDataBlock(fd, (unsigned char *) buf) == 0)
117             ;
118
119         lpos = LM_to_uint(buf[0], buf[1]);
120         tpos = LM_to_uint(buf[2], buf[3]);
121         width = LM_to_uint(buf[4], buf[5]);
122         height = LM_to_uint(buf[6], buf[7]);
123         cellw = buf[8];
124         cellh = buf[9];
125         foreground = buf[10];
126         background = buf[11];
127
128         while (GetDataBlock(fd, (unsigned char *) buf) != 0) {
129             PPM_ASSIGN(image[ypos][xpos], cmap[CM_RED][v], cmap[CM_GREEN][v],
130                        cmap[CM_BLUE][v]);
131             ++index;
132         }
133
134         return;
135 #else
136         break;
137 #endif
138     case 0xff: /* Application Extension */
139         str = "Application Extension";
140         break;
141     case 0xfe: /* Comment Extension */
142         str = "Comment Extension";
143         while (GetDataBlock(fd, (unsigned char *) buf) != 0) {
144             Fprintf(stderr, "gif comment: %s\n", buf);
145         }
146         return;
147     case 0xf9: /* Graphic Control Extension */
148         str = "Graphic Control Extension";
149         (void) GetDataBlock(fd, (unsigned char *) buf);
150         Gif89.disposal = (buf[0] >> 2) & 0x7;
151         Gif89.inputFlag = (buf[0] >> 1) & 0x1;
152         Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
153         if ((buf[0] & 0x1) != 0)
154             Gif89.transparent = buf[3];
155
156         while (GetDataBlock(fd, (unsigned char *) buf) != 0)
157             ;
158         return;
159     default:
160         str = buf;
161         Sprintf(buf, "UNKNOWN (0x%02x)", label);
162         break;
163     }
164
165     Fprintf(stderr, "got a '%s' extension\n", str);
166
167     while (GetDataBlock(fd, (unsigned char *) buf) != 0)
168         ;
169 }
170
171 static boolean
172 ReadColorMap(fd, number)
173 FILE *fd;
174 int number;
175 {
176     int i;
177     unsigned char rgb[3];
178
179     for (i = 0; i < number; ++i) {
180         if (!ReadOK(fd, rgb, sizeof(rgb))) {
181             return (FALSE);
182         }
183
184         ColorMap[CM_RED][i] = rgb[0];
185         ColorMap[CM_GREEN][i] = rgb[1];
186         ColorMap[CM_BLUE][i] = rgb[2];
187     }
188     colorsinmap = number;
189     return TRUE;
190 }
191
192 /*
193  * Read gif header, including colormaps.  We expect only one image per
194  * file, so if that image has a local colormap, overwrite the global one.
195  */
196 static void
197 read_header(fd)
198 FILE *fd;
199 {
200     unsigned char buf[16];
201     unsigned char c;
202     char version[4];
203
204     if (!ReadOK(fd, buf, 6)) {
205         Fprintf(stderr, "error reading magic number\n");
206         exit(EXIT_FAILURE);
207     }
208
209     if (strncmp((genericptr_t) buf, "GIF", 3) != 0) {
210         Fprintf(stderr, "not a GIF file\n");
211         exit(EXIT_FAILURE);
212     }
213
214     (void) strncpy(version, (char *) buf + 3, 3);
215     version[3] = '\0';
216
217     if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
218         Fprintf(stderr, "bad version number, not '87a' or '89a'\n");
219         exit(EXIT_FAILURE);
220     }
221
222     if (!ReadOK(fd, buf, 7)) {
223         Fprintf(stderr, "failed to read screen descriptor\n");
224         exit(EXIT_FAILURE);
225     }
226
227     GifScreen.Width = LM_to_uint(buf[0], buf[1]);
228     GifScreen.Height = LM_to_uint(buf[2], buf[3]);
229     GifScreen.Colors = 2 << (buf[4] & 0x07);
230     GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
231     GifScreen.Background = buf[5];
232     GifScreen.AspectRatio = buf[6];
233
234     if (BitSet(buf[4], LOCALCOLORMAP)) { /* Global Colormap */
235         if (!ReadColorMap(fd, GifScreen.Colors)) {
236             Fprintf(stderr, "error reading global colormap\n");
237             exit(EXIT_FAILURE);
238         }
239     }
240
241     if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) {
242         Fprintf(stderr, "warning - non-square pixels\n");
243     }
244
245     for (;;) {
246         if (!ReadOK(fd, &c, 1)) {
247             Fprintf(stderr, "EOF / read error on image data\n");
248             exit(EXIT_FAILURE);
249         }
250
251         if (c == ';') { /* GIF terminator */
252             return;
253         }
254
255         if (c == '!') { /* Extension */
256             if (!ReadOK(fd, &c, 1)) {
257                 Fprintf(stderr,
258                         "EOF / read error on extension function code\n");
259                 exit(EXIT_FAILURE);
260             }
261             DoExtension(fd, (int) c);
262             continue;
263         }
264
265         if (c != ',') { /* Not a valid start character */
266             Fprintf(stderr, "bogus character 0x%02x, ignoring\n", (int) c);
267             continue;
268         }
269
270         if (!ReadOK(fd, buf, 9)) {
271             Fprintf(stderr, "couldn't read left/top/width/height\n");
272             exit(EXIT_FAILURE);
273         }
274
275         if (BitSet(buf[8], LOCALCOLORMAP)) {
276             /* replace global color map with local */
277             GifScreen.Colors = 1 << ((buf[8] & 0x07) + 1);
278             if (!ReadColorMap(fd, GifScreen.Colors)) {
279                 Fprintf(stderr, "error reading local colormap\n");
280                 exit(EXIT_FAILURE);
281             }
282         }
283         if (GifScreen.Width != LM_to_uint(buf[4], buf[5])) {
284             Fprintf(stderr, "warning: widths don't match\n");
285             GifScreen.Width = LM_to_uint(buf[4], buf[5]);
286         }
287         if (GifScreen.Height != LM_to_uint(buf[6], buf[7])) {
288             Fprintf(stderr, "warning: heights don't match\n");
289             GifScreen.Height = LM_to_uint(buf[6], buf[7]);
290         }
291         GifScreen.Interlace = BitSet(buf[8], INTERLACE);
292         return;
293     }
294 }
295
296 static int
297 GetCode(fd, code_size, flag)
298 FILE *fd;
299 int code_size;
300 int flag;
301 {
302     static unsigned char buf[280];
303     static int curbit, lastbit, done, last_byte;
304     int i, j, ret;
305     unsigned char count;
306
307     if (flag) {
308         curbit = 0;
309         lastbit = 0;
310         done = FALSE;
311         return 0;
312     }
313
314     if ((curbit + code_size) >= lastbit) {
315         if (done) {
316             if (curbit >= lastbit)
317                 Fprintf(stderr, "ran off the end of my bits\n");
318             return -1;
319         }
320         buf[0] = buf[last_byte - 2];
321         buf[1] = buf[last_byte - 1];
322
323         if ((count = GetDataBlock(fd, &buf[2])) == 0)
324             done = TRUE;
325
326         last_byte = 2 + count;
327         curbit = (curbit - lastbit) + 16;
328         lastbit = (2 + count) * 8;
329     }
330
331     ret = 0;
332     for (i = curbit, j = 0; j < code_size; ++i, ++j)
333         ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
334
335     curbit += code_size;
336
337     return ret;
338 }
339
340 static int
341 LWZReadByte(fd, flag, input_code_size)
342 FILE *fd;
343 int flag;
344 int input_code_size;
345 {
346     static int fresh = FALSE;
347     int code, incode;
348     static int code_size, set_code_size;
349     static int max_code, max_code_size;
350     static int firstcode, oldcode;
351     static int clear_code, end_code;
352     static int table[2][(1 << MAX_LWZ_BITS)];
353     static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
354     register int i;
355
356     if (flag) {
357         set_code_size = input_code_size;
358         code_size = set_code_size + 1;
359         clear_code = 1 << set_code_size;
360         end_code = clear_code + 1;
361         max_code_size = 2 * clear_code;
362         max_code = clear_code + 2;
363
364         (void) GetCode(fd, 0, TRUE);
365
366         fresh = TRUE;
367
368         for (i = 0; i < clear_code; ++i) {
369             table[0][i] = 0;
370             table[1][i] = i;
371         }
372         for (; i < (1 << MAX_LWZ_BITS); ++i)
373             table[0][i] = table[1][0] = 0;
374
375         sp = stack;
376
377         return 0;
378     } else if (fresh) {
379         fresh = FALSE;
380         do {
381             firstcode = oldcode = GetCode(fd, code_size, FALSE);
382         } while (firstcode == clear_code);
383         return firstcode;
384     }
385
386     if (sp > stack)
387         return *--sp;
388
389     while ((code = GetCode(fd, code_size, FALSE)) >= 0) {
390         if (code == clear_code) {
391             for (i = 0; i < clear_code; ++i) {
392                 table[0][i] = 0;
393                 table[1][i] = i;
394             }
395             for (; i < (1 << MAX_LWZ_BITS); ++i)
396                 table[0][i] = table[1][i] = 0;
397             code_size = set_code_size + 1;
398             max_code_size = 2 * clear_code;
399             max_code = clear_code + 2;
400             sp = stack;
401             firstcode = oldcode = GetCode(fd, code_size, FALSE);
402             return firstcode;
403         } else if (code == end_code) {
404             int count;
405             unsigned char buf[260];
406
407             if (ZeroDataBlock)
408                 return -2;
409
410             while ((count = GetDataBlock(fd, buf)) > 0)
411                 ;
412
413             if (count != 0)
414                 Fprintf(stderr,
415                         "missing EOD in data stream (common occurrence)\n");
416             return -2;
417         }
418
419         incode = code;
420
421         if (code >= max_code) {
422             *sp++ = firstcode;
423             code = oldcode;
424         }
425
426         while (code >= clear_code) {
427             *sp++ = table[1][code];
428             if (code == table[0][code]) {
429                 Fprintf(stderr, "circular table entry BIG ERROR\n");
430                 exit(EXIT_FAILURE);
431             }
432             code = table[0][code];
433         }
434
435         *sp++ = firstcode = table[1][code];
436
437         if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
438             table[0][code] = oldcode;
439             table[1][code] = firstcode;
440             ++max_code;
441             if ((max_code >= max_code_size)
442                 && (max_code_size < (1 << MAX_LWZ_BITS))) {
443                 max_code_size *= 2;
444                 ++code_size;
445             }
446         }
447
448         oldcode = incode;
449
450         if (sp > stack)
451             return *--sp;
452     }
453     return code;
454 }
455
456 static void
457 ReadInterleavedImage(fd, len, height)
458 FILE *fd;
459 int len, height;
460 {
461     int v;
462     int xpos = 0, ypos = 0, pass = 0;
463
464     while ((v = LWZReadByte(fd, FALSE, (int) input_code_size)) >= 0) {
465         PPM_ASSIGN(image[ypos][xpos], ColorMap[CM_RED][v],
466                    ColorMap[CM_GREEN][v], ColorMap[CM_BLUE][v]);
467
468         ++xpos;
469         if (xpos == len) {
470             xpos = 0;
471             switch (pass) {
472             case 0:
473             case 1:
474                 ypos += 8;
475                 break;
476             case 2:
477                 ypos += 4;
478                 break;
479             case 3:
480                 ypos += 2;
481                 break;
482             }
483
484             if (ypos >= height) {
485                 ++pass;
486                 switch (pass) {
487                 case 1:
488                     ypos = 4;
489                     break;
490                 case 2:
491                     ypos = 2;
492                     break;
493                 case 3:
494                     ypos = 1;
495                     break;
496                 default:
497                     goto fini;
498                 }
499             }
500         }
501         if (ypos >= height)
502             break;
503     }
504
505 fini:
506     if (LWZReadByte(fd, FALSE, (int) input_code_size) >= 0)
507         Fprintf(stderr, "too much input data, ignoring extra...\n");
508 }
509
510 static void
511 ReadTileStrip(fd, len)
512 FILE *fd;
513 int len;
514 {
515     int v;
516     int xpos = 0, ypos = 0;
517
518     while ((v = LWZReadByte(fd, FALSE, (int) input_code_size)) >= 0) {
519         PPM_ASSIGN(image[ypos][xpos], ColorMap[CM_RED][v],
520                    ColorMap[CM_GREEN][v], ColorMap[CM_BLUE][v]);
521
522         ++xpos;
523         if (xpos == len) {
524             xpos = 0;
525             ++ypos;
526         }
527         if (ypos >= TILE_Y)
528             break;
529     }
530 }
531
532 boolean
533 fopen_gif_file(filename, type)
534 const char *filename;
535 const char *type;
536 {
537     int i;
538
539     if (strcmp(type, RDBMODE)) {
540         Fprintf(stderr, "using reading routine for non-reading?\n");
541         return FALSE;
542     }
543     gif_file = fopen(filename, type);
544     if (gif_file == (FILE *) 0) {
545         Fprintf(stderr, "cannot open gif file %s\n", filename);
546         return FALSE;
547     }
548
549     read_header(gif_file);
550     if (GifScreen.Width % TILE_X) {
551         Fprintf(stderr, "error: width %d not divisible by %d\n",
552                 GifScreen.Width, TILE_X);
553         exit(EXIT_FAILURE);
554     }
555     tiles_across = GifScreen.Width / TILE_X;
556     curr_tiles_across = 0;
557     if (GifScreen.Height % TILE_Y) {
558         Fprintf(stderr, "error: height %d not divisible by %d\n",
559                 GifScreen.Height, TILE_Y);
560         /* exit(EXIT_FAILURE) */;
561     }
562     tiles_down = GifScreen.Height / TILE_Y;
563     curr_tiles_down = 0;
564
565     if (GifScreen.Interlace) {
566         /* sigh -- hope this doesn't happen on micros */
567         image = (pixel **) alloc(GifScreen.Height * sizeof(pixel *));
568         for (i = 0; i < GifScreen.Height; i++) {
569             image[i] = (pixel *) alloc(GifScreen.Width * sizeof(pixel));
570         }
571     } else {
572         image = (pixel **) alloc(TILE_Y * sizeof(pixel *));
573         for (i = 0; i < TILE_Y; i++) {
574             image[i] = (pixel *) alloc(GifScreen.Width * sizeof(pixel));
575         }
576     }
577
578     /*
579     **  Initialize the Compression routines
580     */
581     if (!ReadOK(gif_file, &input_code_size, 1)) {
582         Fprintf(stderr, "EOF / read error on image data\n");
583         exit(EXIT_FAILURE);
584     }
585
586     if (LWZReadByte(gif_file, TRUE, (int) input_code_size) < 0) {
587         Fprintf(stderr, "error reading image\n");
588         exit(EXIT_FAILURE);
589     }
590
591     /* read first section */
592     if (GifScreen.Interlace) {
593         ReadInterleavedImage(gif_file, GifScreen.Width, GifScreen.Height);
594     } else {
595         ReadTileStrip(gif_file, GifScreen.Width);
596     }
597     return TRUE;
598 }
599
600 /* Read a tile.  Returns FALSE when there are no more tiles */
601 boolean
602 read_gif_tile(pixels)
603 pixel (*pixels)[TILE_X];
604 {
605     int i, j;
606
607     if (curr_tiles_down >= tiles_down)
608         return FALSE;
609     if (curr_tiles_across == tiles_across) {
610         curr_tiles_across = 0;
611         curr_tiles_down++;
612         if (curr_tiles_down >= tiles_down)
613             return FALSE;
614         if (!GifScreen.Interlace)
615             ReadTileStrip(gif_file, GifScreen.Width);
616     }
617     if (GifScreen.Interlace) {
618         for (j = 0; j < TILE_Y; j++) {
619             for (i = 0; i < TILE_X; i++) {
620                 pixels[j][i] = image[curr_tiles_down * TILE_Y
621                                      + j][curr_tiles_across * TILE_X + i];
622             }
623         }
624     } else {
625         for (j = 0; j < TILE_Y; j++) {
626             for (i = 0; i < TILE_X; i++) {
627                 pixels[j][i] = image[j][curr_tiles_across * TILE_X + i];
628             }
629         }
630     }
631     curr_tiles_across++;
632
633     /* check for "filler" tile */
634     for (j = 0; j < TILE_Y; j++) {
635         for (i = 0; i < TILE_X && i < 4; i += 2) {
636             if (pixels[j][i].r != ColorMap[CM_RED][0]
637                 || pixels[j][i].g != ColorMap[CM_GREEN][0]
638                 || pixels[j][i].b != ColorMap[CM_BLUE][0]
639                 || pixels[j][i + 1].r != ColorMap[CM_RED][1]
640                 || pixels[j][i + 1].g != ColorMap[CM_GREEN][1]
641                 || pixels[j][i + 1].b != ColorMap[CM_BLUE][1])
642                 return TRUE;
643         }
644     }
645     return FALSE;
646 }
647
648 int
649 fclose_gif_file()
650 {
651     int i;
652
653     if (GifScreen.Interlace) {
654         for (i = 0; i < GifScreen.Height; i++) {
655             free((genericptr_t) image[i]);
656         }
657         free((genericptr_t) image);
658     } else {
659         for (i = 0; i < TILE_Y; i++) {
660             free((genericptr_t) image[i]);
661         }
662         free((genericptr_t) image);
663     }
664     return (fclose(gif_file));
665 }
666
667 #ifndef AMIGA
668 static char *std_args[] = { "tilemap", /* dummy argv[0] */
669                             "monsters.gif", "monsters.txt", "objects.gif",
670                             "objects.txt",  "other.gif",    "other.txt" };
671
672 int
673 main(argc, argv)
674 int argc;
675 char *argv[];
676 {
677     pixel pixels[TILE_Y][TILE_X];
678
679     if (argc == 1) {
680         argc = SIZE(std_args);
681         argv = std_args;
682     } else if (argc != 3) {
683         Fprintf(stderr, "usage: gif2txt giffile txtfile\n");
684         exit(EXIT_FAILURE);
685     }
686
687     while (argc > 1) {
688         if (!fopen_gif_file(argv[1], RDBMODE))
689             exit(EXIT_FAILURE);
690
691         init_colormap();
692
693         if (!fopen_text_file(argv[2], WRTMODE)) {
694             (void) fclose_gif_file();
695             exit(EXIT_FAILURE);
696         }
697
698         while (read_gif_tile(pixels))
699             (void) write_text_tile(pixels);
700
701         (void) fclose_gif_file();
702         (void) fclose_text_file();
703
704         argc -= 2;
705         argv += 2;
706     }
707     exit(EXIT_SUCCESS);
708     /*NOTREACHED*/
709     return 0;
710 }
711 #endif