1 /*
2 **  Copyright 1994, Home Pages, Inc.
3 **
4 **    Please read the file COPYRIGHT for specific information.
5 **
6 **    Home Pages, Inc.
7 **    257 Castro St. Suite 219
8 **    Mountain View, CA 94041
9 **
10 **    Phone: 1 415 903 5353
11 **      Fax: 1 415 903 5345
12 **
13 **    EMail: support@homepages.com
14 **
15 */
16 
17 /* +-------------------------------------------------------------------+ */
18 /* | Copyright 1990 - 1994, David Koblas. (koblas@netcom.com)          | */
19 /* |   Permission to use, copy, modify, and distribute this software   | */
20 /* |   and its documentation for any purpose and without fee is hereby | */
21 /* |   granted, provided that the above copyright notice appear in all | */
22 /* |   copies and that both that copyright notice and this permission  | */
23 /* |   notice appear in supporting documentation.  This software is    | */
24 /* |   provided "as is" without express or implied warranty.           | */
25 /* +-------------------------------------------------------------------+ */
26 
27 #include <stdio.h>
28 #include <setjmp.h>
29 
30 #include "gif.h"
31 
32 #define        TRUE    1
33 #define        FALSE   0
34 
35 #define        MAX_LWZ_BITS            12
36 
37 #define INTERLACE		0x40
38 #define LOCALCOLORMAP		0x80
39 
40 #define BitSet(byte, bit)	(((byte) & (bit)) == (bit))
41 #define	ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
42 #define MKINT(a,b)		(((b)<<8)|(a))
43 #define NEW(x)			((x *)malloc(sizeof(x)))
44 
45 /***************************************************************************
46 *
47 *  ERROR()    --  should not return
48 *  INFO_MSG() --  info message, can be ignored
49 *
50 ***************************************************************************/
51 
52 #if 0
53 #define INFO_MSG(fmt)	pm_message fmt
54 #define ERROR(str)	pm_error(str)
55 #else
56 #if 0
57 #define INFO_MSG(fmt)
58 #define ERROR(str) 	do { RWSetMsg(str); longjmp(setjmp_buffer, 1); } while(0)
59 #else
60 #define INFO_MSG(fmt)
61 #define ERROR(str) 	longjmp(setjmp_buffer, 1)
62 #endif
63 #endif
64 
65 /***************************************************************************/
66 
67 static int readColorMap(FILE *, int, unsigned char [GIF_MAXCOLORS][3]);
68 static int GetDataBlock(FILE *, unsigned char *);
69 static void readImage(FILE *, int, int, int, unsigned char *);
70 
71 static jmp_buf                  setjmp_buffer;
72 
73 static int    verbose = FALSE;
74 static int    showComment = FALSE;
75 
GIFTest(char * file)76 int     GIFTest(char *file)
77 {
78         FILE    *fd = fopen(file, "rb");
79         char    buf[10];
80         int     ret = FALSE;
81 
82         if (fd != NULL && ReadOK(fd, buf, 6)) {
83                 if ((strncmp(buf, "GIF", 3) == 0) &&
84                      ((strncmp(buf + 3, "87a", 3) != 0) ||
85                       (strncmp(buf + 3, "89a", 3) != 0)))
86                         ret = TRUE;
87         }
88 
89         fclose(fd);
90 
91         return ret;
92 }
93 
GIFReadFP(FILE * fd)94 GIFStream *GIFReadFP(FILE *fd)
95 {
96 	unsigned char   buf[256];
97 	unsigned char   c;
98 	GIFStream	*stream;
99 	GIFData		*cur, **end;
100 	GIF89info	info;
101 	int		resetInfo = TRUE;
102 	int		n;
103 
104 	if (fd == NULL)
105 		return NULL;
106 
107 	if (setjmp(setjmp_buffer))
108 		goto out;
109 
110 	if (! ReadOK(fd,buf,6))
111 	       ERROR("error reading magic number" );
112 
113 	if (strncmp((char*)buf,"GIF",3) != 0)
114 	       ERROR("not a GIF file" );
115 
116 	if ((strncmp(buf + 3, "87a", 3) != 0) &&
117 	    (strncmp(buf + 3, "89a", 3) != 0))
118 		ERROR("bad version number, not '87a' or '89a'" );
119 
120 	if (! ReadOK(fd,buf,7))
121 		ERROR("failed to read screen descriptor");
122 
123 	stream = NEW(GIFStream);
124 
125 	stream->width           = MKINT(buf[0], buf[1]);
126 	stream->height          = MKINT(buf[2], buf[3]);
127 
128 	stream->cmapSize        = 2 << (buf[4] & 0x07);
129 	stream->colorMapSize    = stream->cmapSize;
130 	stream->colorResolution = ((int)(buf[4] & 0x70) >> 3) + 1;
131 	stream->background      = buf[5];
132 	stream->aspectRatio     = buf[6];
133 
134 	stream->data            = NULL;
135 
136 	end = &stream->data;
137 
138 	/*
139 	**  Global colormap is present.
140 	*/
141 	if (BitSet(buf[4], LOCALCOLORMAP)) {
142 		if (readColorMap(fd, stream->cmapSize, stream->cmapData))
143 			ERROR("unable to get global colormap");
144 	} else {
145 		stream->cmapSize   = 0;
146 		stream->background = -1;
147 	}
148 
149 	if (stream->aspectRatio != 0 && stream->aspectRatio != 49) {
150 	       float   r;
151 
152 	       r = ((float) stream->aspectRatio + 15.0) / 64.0;
153 	       INFO_MSG(("warning - non-square pixels; to fix do a 'pnmscale -%cscale %g'",
154 		   r < 1.0 ? 'x' : 'y',
155 		   r < 1.0 ? 1.0 / r : r ));
156 	}
157 
158 	while (ReadOK(fd, &c, 1) && c != ';') {
159 		if (resetInfo) {
160 			info.disposal    = 0;
161 			info.inputFlag   = 0;
162 			info.delayTime   = 0;
163 			info.transparent = -1;
164 			resetInfo = FALSE;
165 		}
166 		cur = NULL;
167 
168 		if (c == '!') {		/* Extension */
169 			if (! ReadOK(fd,&c,1))
170 				ERROR("EOF / read error on extention function code");
171 			if (c == 0xf9) {	/* graphic control */
172 				(void) GetDataBlock(fd, buf);
173 				info.disposal    = (buf[0] >> 2) & 0x7;
174 				info.inputFlag   = (buf[0] >> 1) & 0x1;
175 				info.delayTime   = MKINT(buf[1],buf[2]);
176 				if (BitSet(buf[0], 0x1))
177 					info.transparent = buf[3];
178 
179 				while (GetDataBlock(fd,  buf) != 0)
180 					;
181 			} else if (c == 0xfe || c == 0x01) {
182 				int		len = 0;
183 				int		size = 256;
184 				char		*text = NULL;
185 
186 				/*
187 				**  Comment or Plain Text
188 				*/
189 
190 				cur = NEW(GIFData);
191 
192 				if (c == 0x01) {
193 					(void)GetDataBlock(fd, buf);
194 
195 					cur->type   = gif_text;
196 					cur->info   = info;
197 					cur->x      = MKINT(buf[0],buf[1]);
198 					cur->y      = MKINT(buf[2],buf[3]);
199 					cur->width  = MKINT(buf[4],buf[5]);
200 					cur->height = MKINT(buf[6],buf[7]);
201 
202 					cur->data.text.cellWidth  = buf[8];
203 					cur->data.text.cellHeight = buf[9];
204 					cur->data.text.fg         = buf[10];
205 					cur->data.text.bg         = buf[11];
206 
207 					resetInfo = TRUE;
208 				} else {
209 					cur->type    = gif_comment;
210 				}
211 
212 				text = (char*)malloc(size);
213 
214 				while ((n = GetDataBlock(fd, buf)) != 0) {
215 					if (n + len >= size)
216 						text = (char*)realloc(text, size += 256);
217 					memcpy(text + len, buf, n);
218 					len += n;
219 				}
220 
221 				if (c == 0x01) {
222 					cur->data.text.len  = len;
223 					cur->data.text.text = text;
224 				} else {
225 					cur->data.comment.len  = len;
226 					cur->data.comment.text = text;
227 				}
228 			} else {
229 				/*
230 				**  Unrecogonized extension, consume it.
231 				*/
232 				while (GetDataBlock(fd, buf) > 0)
233 					;
234 			}
235 		} else if (c == ',') {
236 			if (! ReadOK(fd,buf,9))
237 			       ERROR("couldn't read left/top/width/height");
238 
239 			cur = NEW(GIFData);
240 
241 			cur->type   = gif_image;
242 			cur->info   = info;
243 			cur->x      = MKINT(buf[0], buf[1]);
244 			cur->y      = MKINT(buf[2], buf[3]);
245 			cur->width  = MKINT(buf[4], buf[5]);
246 			cur->height = MKINT(buf[6], buf[7]);
247 			cur->data.image.cmapSize = 1 << ((buf[8] & 0x07) + 1);
248 			if (BitSet(buf[8], LOCALCOLORMAP)) {
249 				if (readColorMap(fd, cur->data.image.cmapSize,
250 					             cur->data.image.cmapData))
251 					ERROR("unable to get local colormap");
252 			} else {
253 				cur->data.image.cmapSize = 0;
254 
255 			}
256 			cur->data.image.data = (unsigned char *)malloc(cur->width * cur->height + 1);
257 			cur->data.image.interlaced = BitSet(buf[8], INTERLACE);
258 			readImage(fd, BitSet(buf[8], INTERLACE),
259 				cur->width, cur->height, cur->data.image.data);
260 
261 			resetInfo = TRUE;
262 		} else {
263 			INFO_MSG(("bogus character 0x%02x, ignoring", (int)c));
264 		}
265 
266 		if (cur != NULL) {
267 			*end = cur;
268 			end = &cur->next;
269 			cur->next = NULL;
270 		}
271 	}
272 
273 	if (c != ';')
274 		ERROR("EOF / data stream" );
275 
276 out:
277 
278 	return stream;
279 }
280 
GIFRead(char * file)281 GIFStream *GIFRead(char *file)
282 {
283 	FILE		*fp = fopen(file, "rb");
284 	GIFStream	*stream = NULL;
285 
286 	if (fp != NULL) {
287 		stream = GIFReadFP(fp);
288 		fclose(fp);
289 	}
290 	return stream;
291 }
292 
readColorMap(FILE * fd,int size,unsigned char data[GIF_MAXCOLORS][3])293 static int readColorMap(FILE *fd, int size,
294 			unsigned char data[GIF_MAXCOLORS][3])
295 {
296 	int             i;
297 	unsigned char   rgb[3 * GIF_MAXCOLORS];
298 	unsigned char	*cp = rgb;
299 
300 	if (! ReadOK(fd, rgb, size * 3))
301 		return TRUE;
302 
303 	for (i = 0; i < size; i++) {
304 		data[i][0] = *cp++;
305 		data[i][1] = *cp++;
306 		data[i][2] = *cp++;
307 	}
308 
309 	return FALSE;
310 }
311 
312 /*
313 **
314 */
315 
316 static int    ZeroDataBlock = FALSE;
317 
GetDataBlock(FILE * fd,unsigned char * buf)318 static int GetDataBlock(FILE *fd, unsigned char *buf)
319 {
320        unsigned char   count;
321 
322        if (! ReadOK(fd,&count,1)) {
323                INFO_MSG(("error in getting DataBlock size"));
324                return -1;
325        }
326 
327        ZeroDataBlock = count == 0;
328 
329        if ((count != 0) && (! ReadOK(fd, buf, count))) {
330                INFO_MSG(("error in reading DataBlock"));
331                return -1;
332        }
333 
334        return count;
335 }
336 
337 /*
338 **
339 **
340 */
341 
342 /*
343 **  Pulled out of nextCode
344 */
345 static	int		curbit, lastbit, get_done, last_byte;
346 static	int		return_clear;
347 /*
348 **  Out of nextLWZ
349 */
350 static int      stack[(1<<(MAX_LWZ_BITS))*2], *sp;
351 static int      code_size, set_code_size;
352 static int      max_code, max_code_size;
353 static int      clear_code, end_code;
354 
initLWZ(int input_code_size)355 static void initLWZ(int input_code_size)
356 {
357 	static int	inited = FALSE;
358 
359 	set_code_size = input_code_size;
360 	code_size     = set_code_size + 1;
361 	clear_code    = 1 << set_code_size ;
362 	end_code      = clear_code + 1;
363 	max_code_size = 2 * clear_code;
364 	max_code      = clear_code + 2;
365 
366 	curbit = lastbit = 0;
367 	last_byte = 2;
368 	get_done = FALSE;
369 
370 	return_clear = TRUE;
371 
372 	sp = stack;
373 }
374 
nextCode(FILE * fd,int code_size)375 static int nextCode(FILE *fd, int code_size)
376 {
377 	static unsigned char    buf[280];
378 	static int maskTbl[16] = {
379 		0x0000, 0x0001, 0x0003, 0x0007,
380 		0x000f, 0x001f, 0x003f, 0x007f,
381 		0x00ff, 0x01ff, 0x03ff, 0x07ff,
382 		0x0fff, 0x1fff, 0x3fff, 0x7fff,
383 	};
384 	int                     i, j, ret, end;
385 
386 	if (return_clear) {
387 		return_clear = FALSE;
388 		return clear_code;
389 	}
390 
391 	end = curbit + code_size;
392 
393 	if (end >= lastbit) {
394 		int	count;
395 
396 		if (get_done) {
397 			if (curbit >= lastbit)
398 				ERROR("ran off the end of my bits" );
399 			return -1;
400 		}
401 		buf[0] = buf[last_byte-2];
402 		buf[1] = buf[last_byte-1];
403 
404 		if ((count = GetDataBlock(fd, &buf[2])) == 0)
405 			get_done = TRUE;
406 
407 		last_byte = 2 + count;
408 		curbit = (curbit - lastbit) + 16;
409 		lastbit = (2+count)*8 ;
410 
411 		end = curbit + code_size;
412 	}
413 
414 	j = end / 8;
415 	i = curbit / 8;
416 
417         if (i == j)
418                 ret = buf[i];
419         else if (i + 1 == j)
420                 ret = buf[i] | (buf[i+1] << 8);
421         else
422                 ret = buf[i] | (buf[i+1] << 8) | (buf[i+2] << 16);
423 
424         ret = (ret >> (curbit % 8)) & maskTbl[code_size];
425 
426 	curbit += code_size;
427 
428 	return ret;
429 }
430 
431 #define readLWZ(fd) ((sp > stack) ? *--sp : nextLWZ(fd))
432 
nextLWZ(FILE * fd)433 static int nextLWZ(FILE *fd)
434 {
435        static int	table[2][(1<< MAX_LWZ_BITS)];
436        static int	firstcode, oldcode;
437        int		code, incode;
438        register int	i;
439 
440        while ((code = nextCode(fd, code_size)) >= 0) {
441                if (code == clear_code) {
442                        for (i = 0; i < clear_code; ++i) {
443                                table[0][i] = 0;
444                                table[1][i] = i;
445                        }
446                        for (; i < (1<<MAX_LWZ_BITS); ++i)
447                                table[0][i] = table[1][i] = 0;
448                        code_size = set_code_size+1;
449                        max_code_size = 2*clear_code;
450                        max_code = clear_code+2;
451                        sp = stack;
452 			do {
453 			       firstcode = oldcode = nextCode(fd, code_size);
454 			} while (firstcode == clear_code);
455 
456 			return firstcode;
457                }
458 	       if (code == end_code) {
459                        int             count;
460                        unsigned char   buf[260];
461 
462                        if (ZeroDataBlock)
463                                return -2;
464 
465                        while ((count = GetDataBlock(fd, buf)) > 0)
466                                ;
467 
468                        if (count != 0)
469                                INFO_MSG(("missing EOD in data stream"));
470                        return -2;
471                }
472 
473                incode = code;
474 
475                if (code >= max_code) {
476                        *sp++ = firstcode;
477                        code = oldcode;
478                }
479 
480                while (code >= clear_code) {
481                        *sp++ = table[1][code];
482                        if (code == table[0][code])
483                                ERROR("circular table entry BIG ERROR");
484                        code = table[0][code];
485                }
486 
487                *sp++ = firstcode = table[1][code];
488 
489                if ((code = max_code) <(1<<MAX_LWZ_BITS)) {
490                        table[0][code] = oldcode;
491                        table[1][code] = firstcode;
492                        ++max_code;
493                        if ((max_code >= max_code_size) &&
494                                (max_code_size < (1<<MAX_LWZ_BITS))) {
495                                max_code_size *= 2;
496                                ++code_size;
497                        }
498                }
499 
500                oldcode = incode;
501 
502                if (sp > stack)
503                        return *--sp;
504        }
505        return code;
506 }
507 
readImage(FILE * fd,int interlace,int width,int height,unsigned char * data)508 static void readImage(FILE *fd, int interlace, int width, int height,
509 			unsigned char *data)
510 {
511        unsigned char	*dp, c;
512        int		v, xpos = 0, ypos = 0, pass = 0;
513 
514 	/*
515 	**  Initialize the Compression routines
516 	*/
517 	if (! ReadOK(fd,&c,1))
518 		ERROR("EOF / read error on image data" );
519 
520 	initLWZ(c);
521 
522 	if (verbose)
523 		INFO_MSG(("reading %d by %d%s GIF image",
524 			width, height, interlace ? " interlaced" : ""));
525 
526 	if (interlace) {
527 		int	i;
528 		int	pass = 0, step = 8;
529 
530 		for (i = 0; i < height; i++) {
531 			dp = &data[width * ypos];
532 			for (xpos = 0; xpos < width; xpos++) {
533 				if ((v = readLWZ(fd)) < 0)
534 					goto fini;
535 
536 				*dp++ = v;
537 			}
538 			if ((ypos += step) >= height) {
539 				do {
540 					if (pass++ > 0)
541 						step /= 2;
542 					ypos = step / 2;
543 				} while (ypos > height);
544 			}
545 		}
546 	} else {
547 		dp = data;
548 		for (ypos = 0; ypos < height; ypos++) {
549 			for (xpos = 0; xpos < width; xpos++) {
550 				if ((v = readLWZ(fd)) < 0)
551 					goto fini;
552 
553 				*dp++ = v;
554 			}
555 		}
556 	}
557 
558 fini:
559        if (readLWZ(fd) >= 0)
560                INFO_MSG(("too much input data, ignoring extra..."));
561 
562        return;
563 }
564