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