NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / win / share / gifread.c
blob683bf69d56ad17566bf699f235c65026b6b8b406
1 /* GIF reading routines based on those in pbmplus:ppm/giftoppm.c, bearing
2 * following copyright notice:
3 */
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 /* +-------------------------------------------------------------------+ */
16 * $ANH-Date: 1432512803 2015/05/25 00:13:23 $ $ANH-Branch: master $:$ANH-Revision: 1.5 $
19 #include "config.h"
20 #include "tile.h"
22 #ifndef MONITOR_HEAP
23 extern long *FDECL(alloc, (unsigned int));
24 #endif
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)
33 #define MAX_LWZ_BITS 12
35 #define INTERLACE 0x40
36 #define LOCALCOLORMAP 0x80
37 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
39 #define ReadOK(file, buffer, len) \
40 (fread((genericptr_t) buffer, (int) len, 1, file) != 0)
42 #define LM_to_uint(a, b) (((b) << 8) | (a))
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;
54 struct {
55 int transparent;
56 int delayTime;
57 int inputFlag;
58 int disposal;
59 } Gif89 = { -1, -1, -1, 0 };
61 int ZeroDataBlock = FALSE;
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;
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));
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);
82 static int
83 GetDataBlock(fd, buf)
84 FILE *fd;
85 unsigned char *buf;
87 unsigned char count;
89 if (!ReadOK(fd, &count, 1)) {
90 Fprintf(stderr, "error in getting DataBlock size\n");
91 return -1;
94 ZeroDataBlock = (count == 0);
96 if ((count != 0) && (!ReadOK(fd, buf, count))) {
97 Fprintf(stderr, "error in reading DataBlock\n");
98 return -1;
101 return count;
104 static void
105 DoExtension(fd, label)
106 FILE *fd;
107 int label;
109 static char buf[256];
110 char *str;
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)
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];
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;
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);
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];
156 while (GetDataBlock(fd, (unsigned char *) buf) != 0)
158 return;
159 default:
160 str = buf;
161 Sprintf(buf, "UNKNOWN (0x%02x)", label);
162 break;
165 Fprintf(stderr, "got a '%s' extension\n", str);
167 while (GetDataBlock(fd, (unsigned char *) buf) != 0)
171 static boolean
172 ReadColorMap(fd, number)
173 FILE *fd;
174 int number;
176 int i;
177 unsigned char rgb[3];
179 for (i = 0; i < number; ++i) {
180 if (!ReadOK(fd, rgb, sizeof(rgb))) {
181 return (FALSE);
184 ColorMap[CM_RED][i] = rgb[0];
185 ColorMap[CM_GREEN][i] = rgb[1];
186 ColorMap[CM_BLUE][i] = rgb[2];
188 colorsinmap = number;
189 return TRUE;
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.
196 static void
197 read_header(fd)
198 FILE *fd;
200 unsigned char buf[16];
201 unsigned char c;
202 char version[4];
204 if (!ReadOK(fd, buf, 6)) {
205 Fprintf(stderr, "error reading magic number\n");
206 exit(EXIT_FAILURE);
209 if (strncmp((genericptr_t) buf, "GIF", 3) != 0) {
210 Fprintf(stderr, "not a GIF file\n");
211 exit(EXIT_FAILURE);
214 (void) strncpy(version, (char *) buf + 3, 3);
215 version[3] = '\0';
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);
222 if (!ReadOK(fd, buf, 7)) {
223 Fprintf(stderr, "failed to read screen descriptor\n");
224 exit(EXIT_FAILURE);
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];
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);
241 if (GifScreen.AspectRatio != 0 && GifScreen.AspectRatio != 49) {
242 Fprintf(stderr, "warning - non-square pixels\n");
245 for (;;) {
246 if (!ReadOK(fd, &c, 1)) {
247 Fprintf(stderr, "EOF / read error on image data\n");
248 exit(EXIT_FAILURE);
251 if (c == ';') { /* GIF terminator */
252 return;
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);
261 DoExtension(fd, (int) c);
262 continue;
265 if (c != ',') { /* Not a valid start character */
266 Fprintf(stderr, "bogus character 0x%02x, ignoring\n", (int) c);
267 continue;
270 if (!ReadOK(fd, buf, 9)) {
271 Fprintf(stderr, "couldn't read left/top/width/height\n");
272 exit(EXIT_FAILURE);
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);
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]);
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]);
291 GifScreen.Interlace = BitSet(buf[8], INTERLACE);
292 return;
296 static int
297 GetCode(fd, code_size, flag)
298 FILE *fd;
299 int code_size;
300 int flag;
302 static unsigned char buf[280];
303 static int curbit, lastbit, done, last_byte;
304 int i, j, ret;
305 unsigned char count;
307 if (flag) {
308 curbit = 0;
309 lastbit = 0;
310 done = FALSE;
311 return 0;
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;
320 buf[0] = buf[last_byte - 2];
321 buf[1] = buf[last_byte - 1];
323 if ((count = GetDataBlock(fd, &buf[2])) == 0)
324 done = TRUE;
326 last_byte = 2 + count;
327 curbit = (curbit - lastbit) + 16;
328 lastbit = (2 + count) * 8;
331 ret = 0;
332 for (i = curbit, j = 0; j < code_size; ++i, ++j)
333 ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
335 curbit += code_size;
337 return ret;
340 static int
341 LWZReadByte(fd, flag, input_code_size)
342 FILE *fd;
343 int flag;
344 int input_code_size;
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;
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;
364 (void) GetCode(fd, 0, TRUE);
366 fresh = TRUE;
368 for (i = 0; i < clear_code; ++i) {
369 table[0][i] = 0;
370 table[1][i] = i;
372 for (; i < (1 << MAX_LWZ_BITS); ++i)
373 table[0][i] = table[1][0] = 0;
375 sp = stack;
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;
386 if (sp > stack)
387 return *--sp;
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;
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];
407 if (ZeroDataBlock)
408 return -2;
410 while ((count = GetDataBlock(fd, buf)) > 0)
413 if (count != 0)
414 Fprintf(stderr,
415 "missing EOD in data stream (common occurrence)\n");
416 return -2;
419 incode = code;
421 if (code >= max_code) {
422 *sp++ = firstcode;
423 code = oldcode;
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);
432 code = table[0][code];
435 *sp++ = firstcode = table[1][code];
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;
448 oldcode = incode;
450 if (sp > stack)
451 return *--sp;
453 return code;
456 static void
457 ReadInterleavedImage(fd, len, height)
458 FILE *fd;
459 int len, height;
461 int v;
462 int xpos = 0, ypos = 0, pass = 0;
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]);
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;
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;
501 if (ypos >= height)
502 break;
505 fini:
506 if (LWZReadByte(fd, FALSE, (int) input_code_size) >= 0)
507 Fprintf(stderr, "too much input data, ignoring extra...\n");
510 static void
511 ReadTileStrip(fd, len)
512 FILE *fd;
513 int len;
515 int v;
516 int xpos = 0, ypos = 0;
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]);
522 ++xpos;
523 if (xpos == len) {
524 xpos = 0;
525 ++ypos;
527 if (ypos >= TILE_Y)
528 break;
532 boolean
533 fopen_gif_file(filename, type)
534 const char *filename;
535 const char *type;
537 int i;
539 if (strcmp(type, RDBMODE)) {
540 Fprintf(stderr, "using reading routine for non-reading?\n");
541 return FALSE;
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;
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);
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) */;
562 tiles_down = GifScreen.Height / TILE_Y;
563 curr_tiles_down = 0;
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));
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));
579 ** Initialize the Compression routines
581 if (!ReadOK(gif_file, &input_code_size, 1)) {
582 Fprintf(stderr, "EOF / read error on image data\n");
583 exit(EXIT_FAILURE);
586 if (LWZReadByte(gif_file, TRUE, (int) input_code_size) < 0) {
587 Fprintf(stderr, "error reading image\n");
588 exit(EXIT_FAILURE);
591 /* read first section */
592 if (GifScreen.Interlace) {
593 ReadInterleavedImage(gif_file, GifScreen.Width, GifScreen.Height);
594 } else {
595 ReadTileStrip(gif_file, GifScreen.Width);
597 return TRUE;
600 /* Read a tile. Returns FALSE when there are no more tiles */
601 boolean
602 read_gif_tile(pixels)
603 pixel (*pixels)[TILE_X];
605 int i, j;
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);
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];
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];
631 curr_tiles_across++;
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;
645 return FALSE;
649 fclose_gif_file()
651 int i;
653 if (GifScreen.Interlace) {
654 for (i = 0; i < GifScreen.Height; i++) {
655 free((genericptr_t) image[i]);
657 free((genericptr_t) image);
658 } else {
659 for (i = 0; i < TILE_Y; i++) {
660 free((genericptr_t) image[i]);
662 free((genericptr_t) image);
664 return (fclose(gif_file));
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" };
673 main(argc, argv)
674 int argc;
675 char *argv[];
677 pixel pixels[TILE_Y][TILE_X];
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);
687 while (argc > 1) {
688 if (!fopen_gif_file(argv[1], RDBMODE))
689 exit(EXIT_FAILURE);
691 init_colormap();
693 if (!fopen_text_file(argv[2], WRTMODE)) {
694 (void) fclose_gif_file();
695 exit(EXIT_FAILURE);
698 while (read_gif_tile(pixels))
699 (void) write_text_tile(pixels);
701 (void) fclose_gif_file();
702 (void) fclose_text_file();
704 argc -= 2;
705 argv += 2;
707 exit(EXIT_SUCCESS);
708 /*NOTREACHED*/
709 return 0;
711 #endif