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