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