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