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