xref: /PHP-5.3/ext/gd/libgd/gd_gif_in.c (revision c4b5196d)
1 #include <stdio.h>
2 #include <math.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include "gd.h"
6 
7 #include "php.h"
8 
9 /* Used only when debugging GIF compression code */
10 /* #define DEBUGGING_ENVARS */
11 
12 #ifdef DEBUGGING_ENVARS
13 
14 static int verbose_set = 0;
15 static int verbose;
16 #define VERBOSE (verbose_set?verbose:set_verbose())
17 
set_verbose(void)18 static int set_verbose(void)
19 {
20 	verbose = !!getenv("GIF_VERBOSE");
21 	verbose_set = 1;
22 	return(verbose);
23 }
24 
25 #else
26 
27 #define VERBOSE 0
28 
29 #endif
30 
31 
32 #define        MAXCOLORMAPSIZE         256
33 
34 #define        TRUE    1
35 #define        FALSE   0
36 
37 #define CM_RED         0
38 #define CM_GREEN       1
39 #define CM_BLUE                2
40 
41 #define        MAX_LWZ_BITS            12
42 
43 #define INTERLACE              0x40
44 #define LOCALCOLORMAP  0x80
45 #define BitSet(byte, bit)      (((byte) & (bit)) == (bit))
46 
47 #define        ReadOK(file,buffer,len) (gdGetBuf(buffer, len, file) > 0)
48 
49 #define LM_to_uint(a,b)                        (((b)<<8)|(a))
50 
51 /* We may eventually want to use this information, but def it out for now */
52 #if 0
53 static struct {
54 	unsigned int    Width;
55 	unsigned int    Height;
56 	unsigned char   ColorMap[3][MAXCOLORMAPSIZE];
57 	unsigned int    BitPixel;
58 	unsigned int    ColorResolution;
59 	unsigned int    Background;
60 	unsigned int    AspectRatio;
61 } GifScreen;
62 #endif
63 
64 #if 0
65 static struct {
66 	int     transparent;
67 	int     delayTime;
68 	int     inputFlag;
69 	int     disposal;
70 } Gif89 = { -1, -1, -1, 0 };
71 #endif
72 
73 #define STACK_SIZE ((1<<(MAX_LWZ_BITS))*2)
74 
75 typedef struct {
76 	unsigned char    buf[280];
77 	int              curbit, lastbit, done, last_byte;
78 } CODE_STATIC_DATA;
79 
80 typedef struct {
81 	int fresh;
82 	int code_size, set_code_size;
83 	int max_code, max_code_size;
84 	int firstcode, oldcode;
85 	int clear_code, end_code;
86 	int table[2][(1<< MAX_LWZ_BITS)];
87 	int stack[STACK_SIZE], *sp;
88 	CODE_STATIC_DATA scd;
89 } LZW_STATIC_DATA;
90 
91 static int ReadColorMap (gdIOCtx *fd, int number, unsigned char (*buffer)[256]);
92 static int DoExtension (gdIOCtx *fd, int label, int *Transparent, int *ZeroDataBlockP);
93 static int GetDataBlock (gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP);
94 static int GetCode (gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP);
95 static int LWZReadByte (gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, int *ZeroDataBlockP);
96 
97 static void ReadImage (gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int *ZeroDataBlockP); /*1.4//, int ignore); */
98 
gdImageCreateFromGifSource(gdSourcePtr inSource)99 gdImagePtr gdImageCreateFromGifSource(gdSourcePtr inSource) /* {{{ */
100 {
101 	gdIOCtx         *in = gdNewSSCtx(inSource, NULL);
102 	gdImagePtr      im;
103 
104 	im = gdImageCreateFromGifCtx(in);
105 
106 	in->gd_free(in);
107 
108 	return im;
109 }
110 /* }}} */
111 
gdImageCreateFromGif(FILE * fdFile)112 gdImagePtr gdImageCreateFromGif(FILE *fdFile) /* {{{ */
113 {
114 	gdIOCtx		*fd = gdNewFileCtx(fdFile);
115 	gdImagePtr    	im = 0;
116 
117 	im = gdImageCreateFromGifCtx(fd);
118 
119 	fd->gd_free(fd);
120 
121 	return im;
122 }
123 /* }}} */
124 
gdImageCreateFromGifCtx(gdIOCtxPtr fd)125 gdImagePtr gdImageCreateFromGifCtx(gdIOCtxPtr fd) /* {{{ */
126 {
127 	int BitPixel;
128 #if 0
129 	int ColorResolution;
130 	int Background;
131 	int AspectRatio;
132 #endif
133 	int Transparent = (-1);
134 	unsigned char   buf[16];
135 	unsigned char   c;
136 	unsigned char   ColorMap[3][MAXCOLORMAPSIZE];
137 	unsigned char   localColorMap[3][MAXCOLORMAPSIZE];
138 	int             imw, imh, screen_width, screen_height;
139 	int             gif87a, useGlobalColormap;
140 	int             bitPixel;
141 	int	       i;
142 	/*1.4//int             imageCount = 0; */
143 
144 	int ZeroDataBlock = FALSE;
145 	int haveGlobalColormap;
146 	gdImagePtr im = 0;
147 
148 	/*1.4//imageNumber = 1; */
149 	if (! ReadOK(fd,buf,6)) {
150 		return 0;
151 	}
152 	if (strncmp((char *)buf,"GIF",3) != 0) {
153 		return 0;
154 	}
155 
156 	if (memcmp((char *)buf+3, "87a", 3) == 0) {
157 		gif87a = 1;
158 	} else if (memcmp((char *)buf+3, "89a", 3) == 0) {
159 		gif87a = 0;
160 	} else {
161 		return 0;
162 	}
163 
164 	if (! ReadOK(fd,buf,7)) {
165 		return 0;
166 	}
167 
168 	BitPixel        = 2<<(buf[4]&0x07);
169 #if 0
170 	ColorResolution = (int) (((buf[4]&0x70)>>3)+1);
171 	Background      = buf[5];
172 	AspectRatio     = buf[6];
173 #endif
174 	screen_width = imw = LM_to_uint(buf[0],buf[1]);
175 	screen_height = imh = LM_to_uint(buf[2],buf[3]);
176 
177 	haveGlobalColormap = BitSet(buf[4], LOCALCOLORMAP);    /* Global Colormap */
178 	if (haveGlobalColormap) {
179 		if (ReadColorMap(fd, BitPixel, ColorMap)) {
180 			return 0;
181 		}
182 	}
183 
184 	for (;;) {
185 		int top, left;
186 		int width, height;
187 
188 		if (! ReadOK(fd,&c,1)) {
189 			return 0;
190 		}
191 		if (c == ';') {         /* GIF terminator */
192 			goto terminated;
193 		}
194 
195 		if (c == '!') {         /* Extension */
196 			if (! ReadOK(fd,&c,1)) {
197 				return 0;
198 			}
199 			DoExtension(fd, c, &Transparent, &ZeroDataBlock);
200 			continue;
201 		}
202 
203 		if (c != ',') {         /* Not a valid start character */
204 			continue;
205 		}
206 
207 		/*1.4//++imageCount; */
208 
209 		if (! ReadOK(fd,buf,9)) {
210 			return 0;
211 		}
212 
213 		useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
214 
215 		bitPixel = 1<<((buf[8]&0x07)+1);
216 		left = LM_to_uint(buf[0], buf[1]);
217 		top = LM_to_uint(buf[2], buf[3]);
218 		width = LM_to_uint(buf[4], buf[5]);
219 		height = LM_to_uint(buf[6], buf[7]);
220 
221 		if (left + width > screen_width || top + height > screen_height) {
222 			if (VERBOSE) {
223 				printf("Frame is not confined to screen dimension.\n");
224 			}
225 			return 0;
226 		}
227 
228 		if (!(im = gdImageCreate(width, height))) {
229 			return 0;
230 		}
231 		im->interlace = BitSet(buf[8], INTERLACE);
232 		if (!useGlobalColormap) {
233 			if (ReadColorMap(fd, bitPixel, localColorMap)) {
234 				gdImageDestroy(im);
235 				return 0;
236 			}
237 			ReadImage(im, fd, width, height, localColorMap,
238 					BitSet(buf[8], INTERLACE), &ZeroDataBlock);
239 		} else {
240 			if (!haveGlobalColormap) {
241 				gdImageDestroy(im);
242 				return 0;
243 			}
244 			ReadImage(im, fd, width, height,
245 						ColorMap,
246 						BitSet(buf[8], INTERLACE), &ZeroDataBlock);
247 		}
248 		if (Transparent != (-1)) {
249 			gdImageColorTransparent(im, Transparent);
250 		}
251 		goto terminated;
252 	}
253 
254 terminated:
255 	/* Terminator before any image was declared! */
256 	if (!im) {
257 		return 0;
258 	}
259 	if (!im->colorsTotal) {
260 		gdImageDestroy(im);
261 		return 0;
262 	}
263 	/* Check for open colors at the end, so
264 	   we can reduce colorsTotal and ultimately
265 	   BitsPerPixel */
266 	for (i=((im->colorsTotal-1)); (i>=0); i--) {
267 		if (im->open[i]) {
268 			im->colorsTotal--;
269 		} else {
270 			break;
271 		}
272 	}
273 	return im;
274 }
275 /* }}} */
276 
ReadColorMap(gdIOCtx * fd,int number,unsigned char (* buffer)[256])277 static int ReadColorMap(gdIOCtx *fd, int number, unsigned char (*buffer)[256]) /* {{{ */
278 {
279 	int             i;
280 	unsigned char   rgb[3];
281 
282 
283 	for (i = 0; i < number; ++i) {
284 		if (! ReadOK(fd, rgb, sizeof(rgb))) {
285 			return TRUE;
286 		}
287 		buffer[CM_RED][i] = rgb[0] ;
288 		buffer[CM_GREEN][i] = rgb[1] ;
289 		buffer[CM_BLUE][i] = rgb[2] ;
290 	}
291 
292 
293 	return FALSE;
294 }
295 /* }}} */
296 
297 static int
DoExtension(gdIOCtx * fd,int label,int * Transparent,int * ZeroDataBlockP)298 DoExtension(gdIOCtx *fd, int label, int *Transparent, int *ZeroDataBlockP)
299 {
300 	unsigned char buf[256];
301 
302 	switch (label) {
303 		case 0xf9:              /* Graphic Control Extension */
304 			memset(buf, 0, 4); /* initialize a few bytes in the case the next function fails */
305                (void) GetDataBlock(fd, (unsigned char*) buf, ZeroDataBlockP);
306 #if 0
307 			Gif89.disposal    = (buf[0] >> 2) & 0x7;
308 			Gif89.inputFlag   = (buf[0] >> 1) & 0x1;
309 			Gif89.delayTime   = LM_to_uint(buf[1],buf[2]);
310 #endif
311 			if ((buf[0] & 0x1) != 0)
312 				*Transparent = buf[3];
313 
314 			while (GetDataBlock(fd, (unsigned char*) buf, ZeroDataBlockP) > 0);
315 			return FALSE;
316 		default:
317 			break;
318 	}
319        while (GetDataBlock(fd, (unsigned char*) buf, ZeroDataBlockP) > 0)
320 		;
321 
322 	return FALSE;
323 }
324 /* }}} */
325 
326 static int
GetDataBlock_(gdIOCtx * fd,unsigned char * buf,int * ZeroDataBlockP)327 GetDataBlock_(gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP)
328 {
329 	unsigned char   count;
330 
331 	if (! ReadOK(fd,&count,1)) {
332 		return -1;
333 	}
334 
335 	*ZeroDataBlockP = count == 0;
336 
337 	if ((count != 0) && (! ReadOK(fd, buf, count))) {
338 		return -1;
339 	}
340 
341 	return count;
342 }
343 /* }}} */
344 
345 static int
GetDataBlock(gdIOCtx * fd,unsigned char * buf,int * ZeroDataBlockP)346 GetDataBlock(gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP)
347 {
348 	int rv;
349 	int i;
350 
351 	rv = GetDataBlock_(fd,buf, ZeroDataBlockP);
352 	if (VERBOSE) {
353 		char *tmp = NULL;
354 		if (rv > 0) {
355 			tmp = safe_emalloc(3 * rv, sizeof(char), 1);
356 			for (i=0;i<rv;i++) {
357 				sprintf(&tmp[3*sizeof(char)*i], " %02x", buf[i]);
358 			}
359 		} else {
360 			tmp = estrdup("");
361 		}
362 		php_gd_error_ex(E_NOTICE, "[GetDataBlock returning %d: %s]", rv, tmp);
363 		efree(tmp);
364 	}
365 	return(rv);
366 }
367 /* }}} */
368 
369 static int
GetCode_(gdIOCtx * fd,CODE_STATIC_DATA * scd,int code_size,int flag,int * ZeroDataBlockP)370 GetCode_(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP)
371 {
372 	int           i, j, ret;
373 	unsigned char count;
374 
375 	if (flag) {
376 		scd->curbit = 0;
377 		scd->lastbit = 0;
378 		scd->last_byte = 0;
379 		scd->done = FALSE;
380 		return 0;
381 	}
382 
383 	if ( (scd->curbit + code_size) >= scd->lastbit) {
384 		if (scd->done) {
385 			if (scd->curbit >= scd->lastbit) {
386 				/* Oh well */
387 			}
388 			return -1;
389 		}
390 		scd->buf[0] = scd->buf[scd->last_byte-2];
391 		scd->buf[1] = scd->buf[scd->last_byte-1];
392 
393                if ((count = GetDataBlock(fd, &scd->buf[2], ZeroDataBlockP)) <= 0)
394 			scd->done = TRUE;
395 
396 		scd->last_byte = 2 + count;
397 		scd->curbit = (scd->curbit - scd->lastbit) + 16;
398 		scd->lastbit = (2+count)*8 ;
399 	}
400 
401 	ret = 0;
402 	for (i = scd->curbit, j = 0; j < code_size; ++i, ++j)
403 		ret |= ((scd->buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
404 
405 	scd->curbit += code_size;
406 	return ret;
407 }
408 
409 static int
GetCode(gdIOCtx * fd,CODE_STATIC_DATA * scd,int code_size,int flag,int * ZeroDataBlockP)410 GetCode(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP)
411 {
412 	int rv;
413 
414  rv = GetCode_(fd, scd, code_size,flag, ZeroDataBlockP);
415  if (VERBOSE) printf("[GetCode(,%d,%d) returning %d]\n",code_size,flag,rv);
416 	return(rv);
417 }
418 /* }}} */
419 
420 static int
LWZReadByte_(gdIOCtx * fd,LZW_STATIC_DATA * sd,char flag,int input_code_size,int * ZeroDataBlockP)421 LWZReadByte_(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, int *ZeroDataBlockP)
422 {
423 	int code, incode, i;
424 
425 	if (flag) {
426 		sd->set_code_size = input_code_size;
427 		sd->code_size = sd->set_code_size+1;
428 		sd->clear_code = 1 << sd->set_code_size ;
429 		sd->end_code = sd->clear_code + 1;
430 		sd->max_code_size = 2*sd->clear_code;
431 		sd->max_code = sd->clear_code+2;
432 
433 		GetCode(fd, &sd->scd, 0, TRUE, ZeroDataBlockP);
434 
435 		sd->fresh = TRUE;
436 
437 		for (i = 0; i < sd->clear_code; ++i) {
438 			sd->table[0][i] = 0;
439 			sd->table[1][i] = i;
440 		}
441 		for (; i < (1<<MAX_LWZ_BITS); ++i)
442 			sd->table[0][i] = sd->table[1][0] = 0;
443 
444 		sd->sp = sd->stack;
445 
446 		return 0;
447 	} else if (sd->fresh) {
448 		sd->fresh = FALSE;
449 		do {
450 			sd->firstcode = sd->oldcode =
451 			GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP);
452 		} while (sd->firstcode == sd->clear_code);
453 		return sd->firstcode;
454 	}
455 
456 	if (sd->sp > sd->stack)
457 		return *--sd->sp;
458 
459 		while ((code = GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP)) >= 0) {
460 		if (code == sd->clear_code) {
461 			for (i = 0; i < sd->clear_code; ++i) {
462 				sd->table[0][i] = 0;
463 				sd->table[1][i] = i;
464 			}
465 			for (; i < (1<<MAX_LWZ_BITS); ++i)
466 				sd->table[0][i] = sd->table[1][i] = 0;
467 			sd->code_size = sd->set_code_size+1;
468 			sd->max_code_size = 2*sd->clear_code;
469 			sd->max_code = sd->clear_code+2;
470 			sd->sp = sd->stack;
471 			sd->firstcode = sd->oldcode =
472 								GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP);
473 			return sd->firstcode;
474 		} else if (code == sd->end_code) {
475 			int             count;
476 			unsigned char   buf[260];
477 
478 			if (*ZeroDataBlockP)
479 				return -2;
480 
481 			while ((count = GetDataBlock(fd, buf, ZeroDataBlockP)) > 0)
482 				;
483 
484 			if (count != 0)
485 				return -2;
486 		}
487 
488 		incode = code;
489 
490 		if (sd->sp == (sd->stack + STACK_SIZE)) {
491 			/* Bad compressed data stream */
492 			return -1;
493 		}
494 
495 		if (code >= sd->max_code) {
496 			*sd->sp++ = sd->firstcode;
497 			code = sd->oldcode;
498 		}
499 
500 		while (code >= sd->clear_code) {
501 			if (sd->sp == (sd->stack + STACK_SIZE)) {
502 				/* Bad compressed data stream */
503 				return -1;
504 			}
505 			*sd->sp++ = sd->table[1][code];
506 			if (code == sd->table[0][code]) {
507 				/* Oh well */
508 			}
509 			code = sd->table[0][code];
510 		}
511 
512 		*sd->sp++ = sd->firstcode = sd->table[1][code];
513 
514 		if ((code = sd->max_code) <(1<<MAX_LWZ_BITS)) {
515 			sd->table[0][code] = sd->oldcode;
516 			sd->table[1][code] = sd->firstcode;
517 			++sd->max_code;
518 			if ((sd->max_code >= sd->max_code_size) &&
519 					(sd->max_code_size < (1<<MAX_LWZ_BITS))) {
520 				sd->max_code_size *= 2;
521 				++sd->code_size;
522 			}
523 		}
524 
525 		sd->oldcode = incode;
526 
527 		if (sd->sp > sd->stack)
528 			return *--sd->sp;
529 	}
530 	return code;
531 }
532 /* }}} */
533 
534 static int
LWZReadByte(gdIOCtx * fd,LZW_STATIC_DATA * sd,char flag,int input_code_size,int * ZeroDataBlockP)535 LWZReadByte(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, int *ZeroDataBlockP)
536 {
537 	int rv;
538 
539  rv = LWZReadByte_(fd, sd, flag, input_code_size, ZeroDataBlockP);
540  if (VERBOSE) printf("[LWZReadByte(,%d,%d) returning %d]\n",flag,input_code_size,rv);
541 	return(rv);
542 }
543 /* }}} */
544 
545 static void
ReadImage(gdImagePtr im,gdIOCtx * fd,int len,int height,unsigned char (* cmap)[256],int interlace,int * ZeroDataBlockP)546 ReadImage(gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int *ZeroDataBlockP) /*1.4//, int ignore) */
547 {
548 	unsigned char   c;
549 	int             v;
550 	int             xpos = 0, ypos = 0, pass = 0;
551 	int i;
552 	LZW_STATIC_DATA sd;
553 
554 
555 	/*
556 	 **  Initialize the Compression routines
557 	 */
558 	if (! ReadOK(fd,&c,1)) {
559 		return;
560 	}
561 
562 	if (c > MAX_LWZ_BITS) {
563 		return;
564 	}
565 
566 	/* Stash the color map into the image */
567 	for (i=0; (i<gdMaxColors); i++) {
568 		im->red[i] = cmap[CM_RED][i];
569 		im->green[i] = cmap[CM_GREEN][i];
570 		im->blue[i] = cmap[CM_BLUE][i];
571 		im->open[i] = 1;
572 	}
573 	/* Many (perhaps most) of these colors will remain marked open. */
574 	im->colorsTotal = gdMaxColors;
575 	if (LWZReadByte(fd, &sd, TRUE, c, ZeroDataBlockP) < 0) {
576 		return;
577 	}
578 
579 	/*
580 	 **  If this is an "uninteresting picture" ignore it.
581 	 **  REMOVED For 1.4
582 	 */
583 	/*if (ignore) { */
584 	/*        while (LWZReadByte(fd, &sd, FALSE, c) >= 0) */
585 	/*                ; */
586 	/*        return; */
587 	/*} */
588 
589 	while ((v = LWZReadByte(fd, &sd, FALSE, c, ZeroDataBlockP)) >= 0) {
590 		if (v >= gdMaxColors) {
591 			v = 0;
592 		}
593 		/* This how we recognize which colors are actually used. */
594 		if (im->open[v]) {
595 			im->open[v] = 0;
596 		}
597 		gdImageSetPixel(im, xpos, ypos, v);
598 		++xpos;
599 		if (xpos == len) {
600 			xpos = 0;
601 			if (interlace) {
602 				switch (pass) {
603 					case 0:
604 					case 1:
605 						ypos += 8; break;
606 					case 2:
607 						ypos += 4; break;
608 					case 3:
609 						ypos += 2; break;
610 				}
611 
612 				if (ypos >= height) {
613 					++pass;
614 					switch (pass) {
615 						case 1:
616 							ypos = 4; break;
617 						case 2:
618 							ypos = 2; break;
619 						case 3:
620 							ypos = 1; break;
621 						default:
622 							goto fini;
623 					}
624 				}
625 			} else {
626 				++ypos;
627 			}
628 		}
629 		if (ypos >= height)
630 			break;
631 	}
632 
633 fini:
634 	if (LWZReadByte(fd, &sd, FALSE, c, ZeroDataBlockP) >=0) {
635 		/* Ignore extra */
636 	}
637 }
638 /* }}} */
639