Back to index

php5  5.3.10
gd_gif_in.c
Go to the documentation of this file.
00001 #include <stdio.h>
00002 #include <math.h>
00003 #include <string.h>
00004 #include <stdlib.h>
00005 #include "gd.h"
00006 
00007 #include "php.h"
00008 
00009 /* Used only when debugging GIF compression code */
00010 /* #define DEBUGGING_ENVARS */
00011 
00012 #ifdef DEBUGGING_ENVARS
00013 
00014 static int verbose_set = 0;
00015 static int verbose;
00016 #define VERBOSE (verbose_set?verbose:set_verbose())
00017 
00018 static int set_verbose(void)
00019 {
00020        verbose = !!getenv("GIF_VERBOSE");
00021        verbose_set = 1;
00022        return(verbose);
00023 }
00024 
00025 #else
00026 
00027 #define VERBOSE 0
00028 
00029 #endif
00030 
00031 
00032 #define        MAXCOLORMAPSIZE         256
00033 
00034 #define        TRUE    1
00035 #define        FALSE   0
00036 
00037 #define CM_RED         0
00038 #define CM_GREEN       1
00039 #define CM_BLUE                2
00040 
00041 #define        MAX_LWZ_BITS            12
00042 
00043 #define INTERLACE              0x40
00044 #define LOCALCOLORMAP  0x80
00045 #define BitSet(byte, bit)      (((byte) & (bit)) == (bit))
00046 
00047 #define        ReadOK(file,buffer,len) (gdGetBuf(buffer, len, file) > 0)
00048 
00049 #define LM_to_uint(a,b)                        (((b)<<8)|(a))
00050 
00051 /* We may eventually want to use this information, but def it out for now */
00052 #if 0
00053 static struct {
00054        unsigned int    Width;
00055        unsigned int    Height;
00056        unsigned char   ColorMap[3][MAXCOLORMAPSIZE];
00057        unsigned int    BitPixel;
00058        unsigned int    ColorResolution;
00059        unsigned int    Background;
00060        unsigned int    AspectRatio;
00061 } GifScreen;
00062 #endif
00063 
00064 #if 0
00065 static struct {
00066        int     transparent;
00067        int     delayTime;
00068        int     inputFlag;
00069        int     disposal;
00070 } Gif89 = { -1, -1, -1, 0 };
00071 #endif
00072 
00073 #define STACK_SIZE ((1<<(MAX_LWZ_BITS))*2)
00074 
00075 typedef struct {
00076        unsigned char    buf[280];
00077        int              curbit, lastbit, done, last_byte;
00078 } CODE_STATIC_DATA;
00079 
00080 typedef struct {
00081        int fresh;
00082        int code_size, set_code_size;
00083        int max_code, max_code_size;
00084        int firstcode, oldcode;
00085        int clear_code, end_code;
00086        int table[2][(1<< MAX_LWZ_BITS)];
00087        int stack[STACK_SIZE], *sp;
00088        CODE_STATIC_DATA scd;
00089 } LZW_STATIC_DATA;
00090 
00091 static int ReadColorMap (gdIOCtx *fd, int number, unsigned char (*buffer)[256]);
00092 static int DoExtension (gdIOCtx *fd, int label, int *Transparent, int *ZeroDataBlockP);
00093 static int GetDataBlock (gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP);
00094 static int GetCode (gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP);
00095 static int LWZReadByte (gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, int *ZeroDataBlockP);
00096 
00097 static void ReadImage (gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int *ZeroDataBlockP); /*1.4//, int ignore); */
00098 
00099 gdImagePtr gdImageCreateFromGifSource(gdSourcePtr inSource) /* {{{ */
00100 {
00101        gdIOCtx         *in = gdNewSSCtx(inSource, NULL);
00102        gdImagePtr      im;
00103 
00104        im = gdImageCreateFromGifCtx(in);
00105 
00106        in->gd_free(in);
00107 
00108        return im;
00109 }
00110 /* }}} */
00111 
00112 gdImagePtr gdImageCreateFromGif(FILE *fdFile) /* {{{ */
00113 {
00114        gdIOCtx              *fd = gdNewFileCtx(fdFile);
00115        gdImagePtr           im = 0;
00116 
00117        im = gdImageCreateFromGifCtx(fd);
00118 
00119        fd->gd_free(fd);
00120 
00121        return im;
00122 }
00123 /* }}} */
00124 
00125 gdImagePtr gdImageCreateFromGifCtx(gdIOCtxPtr fd) /* {{{ */
00126 {
00127        int BitPixel;
00128 #if 0
00129        int ColorResolution;
00130        int Background;
00131        int AspectRatio;
00132 #endif
00133        int Transparent = (-1);
00134        unsigned char   buf[16];
00135        unsigned char   c;
00136        unsigned char   ColorMap[3][MAXCOLORMAPSIZE];
00137        unsigned char   localColorMap[3][MAXCOLORMAPSIZE];
00138        int             imw, imh, screen_width, screen_height;
00139        int             gif87a, useGlobalColormap;
00140        int             bitPixel;
00141        int           i;
00142        /*1.4//int             imageCount = 0; */
00143 
00144        int ZeroDataBlock = FALSE;
00145        int haveGlobalColormap;
00146        gdImagePtr im = 0;
00147 
00148        /*1.4//imageNumber = 1; */
00149        if (! ReadOK(fd,buf,6)) {
00150               return 0;
00151        }
00152        if (strncmp((char *)buf,"GIF",3) != 0) {
00153               return 0;
00154        }
00155 
00156        if (memcmp((char *)buf+3, "87a", 3) == 0) {
00157               gif87a = 1;
00158        } else if (memcmp((char *)buf+3, "89a", 3) == 0) {
00159               gif87a = 0;
00160        } else {
00161               return 0;
00162        }
00163 
00164        if (! ReadOK(fd,buf,7)) {
00165               return 0;
00166        }
00167 
00168        BitPixel        = 2<<(buf[4]&0x07);
00169 #if 0
00170        ColorResolution = (int) (((buf[4]&0x70)>>3)+1);
00171        Background      = buf[5];
00172        AspectRatio     = buf[6];
00173 #endif
00174        screen_width = imw = LM_to_uint(buf[0],buf[1]);
00175        screen_height = imh = LM_to_uint(buf[2],buf[3]);
00176 
00177        haveGlobalColormap = BitSet(buf[4], LOCALCOLORMAP);    /* Global Colormap */
00178        if (haveGlobalColormap) {
00179               if (ReadColorMap(fd, BitPixel, ColorMap)) {
00180                      return 0;
00181               }
00182        }
00183 
00184        for (;;) {
00185               int top, left;
00186               int width, height;
00187 
00188               if (! ReadOK(fd,&c,1)) {
00189                      return 0;
00190               }
00191               if (c == ';') {         /* GIF terminator */
00192                      goto terminated;
00193               }
00194 
00195               if (c == '!') {         /* Extension */
00196                      if (! ReadOK(fd,&c,1)) {
00197                             return 0;
00198                      }
00199                      DoExtension(fd, c, &Transparent, &ZeroDataBlock);
00200                      continue;
00201               }
00202 
00203               if (c != ',') {         /* Not a valid start character */
00204                      continue;
00205               }
00206 
00207               /*1.4//++imageCount; */
00208 
00209               if (! ReadOK(fd,buf,9)) {
00210                      return 0;
00211               }
00212 
00213               useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
00214 
00215               bitPixel = 1<<((buf[8]&0x07)+1);
00216               left = LM_to_uint(buf[0], buf[1]);
00217               top = LM_to_uint(buf[2], buf[3]);
00218               width = LM_to_uint(buf[4], buf[5]);
00219               height = LM_to_uint(buf[6], buf[7]);
00220 
00221               if (left + width > screen_width || top + height > screen_height) {
00222                      if (VERBOSE) {
00223                             printf("Frame is not confined to screen dimension.\n");
00224                      }
00225                      return 0;
00226               }
00227 
00228               if (!(im = gdImageCreate(width, height))) {
00229                      return 0;
00230               }
00231               im->interlace = BitSet(buf[8], INTERLACE);
00232               if (!useGlobalColormap) {
00233                      if (ReadColorMap(fd, bitPixel, localColorMap)) { 
00234                             gdImageDestroy(im);
00235                             return 0;
00236                      }
00237                      ReadImage(im, fd, width, height, localColorMap, 
00238                                    BitSet(buf[8], INTERLACE), &ZeroDataBlock);
00239               } else {
00240                      if (!haveGlobalColormap) {
00241                             gdImageDestroy(im);
00242                             return 0;
00243                      }
00244                      ReadImage(im, fd, width, height,
00245                                           ColorMap, 
00246                                           BitSet(buf[8], INTERLACE), &ZeroDataBlock);
00247               }
00248               if (Transparent != (-1)) {
00249                      gdImageColorTransparent(im, Transparent);
00250               }
00251               goto terminated;
00252        }
00253 
00254 terminated:
00255        /* Terminator before any image was declared! */
00256        if (!im) {
00257               return 0;
00258        }
00259        if (!im->colorsTotal) {
00260               gdImageDestroy(im);
00261               return 0;
00262        }
00263        /* Check for open colors at the end, so
00264           we can reduce colorsTotal and ultimately
00265           BitsPerPixel */
00266        for (i=((im->colorsTotal-1)); (i>=0); i--) {
00267               if (im->open[i]) {
00268                      im->colorsTotal--;
00269               } else {
00270                      break;
00271               }
00272        }
00273        return im;
00274 }
00275 /* }}} */
00276 
00277 static int ReadColorMap(gdIOCtx *fd, int number, unsigned char (*buffer)[256]) /* {{{ */
00278 {
00279        int             i;
00280        unsigned char   rgb[3];
00281 
00282 
00283        for (i = 0; i < number; ++i) {
00284               if (! ReadOK(fd, rgb, sizeof(rgb))) {
00285                      return TRUE;
00286               }
00287               buffer[CM_RED][i] = rgb[0] ;
00288               buffer[CM_GREEN][i] = rgb[1] ;
00289               buffer[CM_BLUE][i] = rgb[2] ;
00290        }
00291 
00292 
00293        return FALSE;
00294 }
00295 /* }}} */
00296 
00297 static int
00298 DoExtension(gdIOCtx *fd, int label, int *Transparent, int *ZeroDataBlockP)
00299 {
00300        unsigned char buf[256];
00301 
00302        switch (label) {
00303               case 0xf9:              /* Graphic Control Extension */
00304                      memset(buf, 0, 4); /* initialize a few bytes in the case the next function fails */
00305                (void) GetDataBlock(fd, (unsigned char*) buf, ZeroDataBlockP);
00306 #if 0
00307                      Gif89.disposal    = (buf[0] >> 2) & 0x7;
00308                      Gif89.inputFlag   = (buf[0] >> 1) & 0x1;
00309                      Gif89.delayTime   = LM_to_uint(buf[1],buf[2]);
00310 #endif
00311                      if ((buf[0] & 0x1) != 0)
00312                             *Transparent = buf[3];
00313 
00314                      while (GetDataBlock(fd, (unsigned char*) buf, ZeroDataBlockP) > 0);
00315                      return FALSE;
00316               default:
00317                      break;
00318        }
00319        while (GetDataBlock(fd, (unsigned char*) buf, ZeroDataBlockP) > 0)
00320               ;
00321 
00322        return FALSE;
00323 }
00324 /* }}} */
00325 
00326 static int
00327 GetDataBlock_(gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP)
00328 {
00329        unsigned char   count;
00330 
00331        if (! ReadOK(fd,&count,1)) {
00332               return -1;
00333        }
00334 
00335        *ZeroDataBlockP = count == 0;
00336 
00337        if ((count != 0) && (! ReadOK(fd, buf, count))) {
00338               return -1;
00339        }
00340 
00341        return count;
00342 }
00343 /* }}} */
00344 
00345 static int
00346 GetDataBlock(gdIOCtx *fd, unsigned char *buf, int *ZeroDataBlockP)
00347 {
00348        int rv;
00349        int i;
00350 
00351        rv = GetDataBlock_(fd,buf, ZeroDataBlockP);
00352        if (VERBOSE) {
00353               char *tmp = NULL;
00354               if (rv > 0) {
00355                      tmp = safe_emalloc(3 * rv, sizeof(char), 1);
00356                      for (i=0;i<rv;i++) {
00357                             sprintf(&tmp[3*sizeof(char)*i], " %02x", buf[i]);
00358                      }
00359               } else {
00360                      tmp = estrdup("");
00361               }
00362               php_gd_error_ex(E_NOTICE, "[GetDataBlock returning %d: %s]", rv, tmp);
00363               efree(tmp);
00364        }
00365        return(rv);
00366 }
00367 /* }}} */
00368 
00369 static int
00370 GetCode_(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP)
00371 {
00372        int           i, j, ret;
00373        unsigned char count;
00374 
00375        if (flag) {
00376               scd->curbit = 0;
00377               scd->lastbit = 0;
00378               scd->last_byte = 0;
00379               scd->done = FALSE;
00380               return 0;
00381        }
00382 
00383        if ( (scd->curbit + code_size) >= scd->lastbit) {
00384               if (scd->done) {
00385                      if (scd->curbit >= scd->lastbit) {
00386                             /* Oh well */
00387                      }
00388                      return -1;
00389               }
00390               scd->buf[0] = scd->buf[scd->last_byte-2];
00391               scd->buf[1] = scd->buf[scd->last_byte-1];
00392 
00393                if ((count = GetDataBlock(fd, &scd->buf[2], ZeroDataBlockP)) <= 0)
00394                      scd->done = TRUE;
00395 
00396               scd->last_byte = 2 + count;
00397               scd->curbit = (scd->curbit - scd->lastbit) + 16;
00398               scd->lastbit = (2+count)*8 ;
00399        }
00400 
00401        ret = 0;
00402        for (i = scd->curbit, j = 0; j < code_size; ++i, ++j)
00403               ret |= ((scd->buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
00404 
00405        scd->curbit += code_size;
00406        return ret;
00407 }
00408 
00409 static int
00410 GetCode(gdIOCtx *fd, CODE_STATIC_DATA *scd, int code_size, int flag, int *ZeroDataBlockP)
00411 {
00412        int rv;
00413 
00414  rv = GetCode_(fd, scd, code_size,flag, ZeroDataBlockP);
00415  if (VERBOSE) printf("[GetCode(,%d,%d) returning %d]\n",code_size,flag,rv);
00416        return(rv);
00417 }
00418 /* }}} */
00419 
00420 static int
00421 LWZReadByte_(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, int *ZeroDataBlockP)
00422 {
00423        int code, incode, i;
00424 
00425        if (flag) {
00426               sd->set_code_size = input_code_size;
00427               sd->code_size = sd->set_code_size+1;
00428               sd->clear_code = 1 << sd->set_code_size ;
00429               sd->end_code = sd->clear_code + 1;
00430               sd->max_code_size = 2*sd->clear_code;
00431               sd->max_code = sd->clear_code+2;
00432 
00433               GetCode(fd, &sd->scd, 0, TRUE, ZeroDataBlockP);
00434 
00435               sd->fresh = TRUE;
00436 
00437               for (i = 0; i < sd->clear_code; ++i) {
00438                      sd->table[0][i] = 0;
00439                      sd->table[1][i] = i;
00440               }
00441               for (; i < (1<<MAX_LWZ_BITS); ++i)
00442                      sd->table[0][i] = sd->table[1][0] = 0;
00443 
00444               sd->sp = sd->stack;
00445 
00446               return 0;
00447        } else if (sd->fresh) {
00448               sd->fresh = FALSE;
00449               do {
00450                      sd->firstcode = sd->oldcode =
00451                      GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP);
00452               } while (sd->firstcode == sd->clear_code);
00453               return sd->firstcode;
00454        }
00455 
00456        if (sd->sp > sd->stack)
00457               return *--sd->sp;
00458 
00459               while ((code = GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP)) >= 0) {
00460               if (code == sd->clear_code) {
00461                      for (i = 0; i < sd->clear_code; ++i) {
00462                             sd->table[0][i] = 0;
00463                             sd->table[1][i] = i;
00464                      }
00465                      for (; i < (1<<MAX_LWZ_BITS); ++i)
00466                             sd->table[0][i] = sd->table[1][i] = 0;
00467                      sd->code_size = sd->set_code_size+1;
00468                      sd->max_code_size = 2*sd->clear_code;
00469                      sd->max_code = sd->clear_code+2;
00470                      sd->sp = sd->stack;
00471                      sd->firstcode = sd->oldcode =
00472                                                         GetCode(fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP);
00473                      return sd->firstcode;
00474               } else if (code == sd->end_code) {
00475                      int             count;
00476                      unsigned char   buf[260];
00477 
00478                      if (*ZeroDataBlockP)
00479                             return -2;
00480 
00481                      while ((count = GetDataBlock(fd, buf, ZeroDataBlockP)) > 0)
00482                             ;
00483 
00484                      if (count != 0)
00485                             return -2;
00486               }
00487 
00488               incode = code;
00489 
00490               if (sd->sp == (sd->stack + STACK_SIZE)) {
00491                      /* Bad compressed data stream */
00492                      return -1;
00493               }
00494 
00495               if (code >= sd->max_code) {
00496                      *sd->sp++ = sd->firstcode;
00497                      code = sd->oldcode;
00498               }
00499 
00500               while (code >= sd->clear_code) {
00501                      if (sd->sp == (sd->stack + STACK_SIZE)) {
00502                             /* Bad compressed data stream */
00503                             return -1;
00504                      }
00505                      *sd->sp++ = sd->table[1][code];
00506                      if (code == sd->table[0][code]) {
00507                             /* Oh well */
00508                      }
00509                      code = sd->table[0][code];
00510               }
00511 
00512               *sd->sp++ = sd->firstcode = sd->table[1][code];
00513 
00514               if ((code = sd->max_code) <(1<<MAX_LWZ_BITS)) {
00515                      sd->table[0][code] = sd->oldcode;
00516                      sd->table[1][code] = sd->firstcode;
00517                      ++sd->max_code;
00518                      if ((sd->max_code >= sd->max_code_size) &&
00519                                    (sd->max_code_size < (1<<MAX_LWZ_BITS))) {
00520                             sd->max_code_size *= 2;
00521                             ++sd->code_size;
00522                      }
00523               }
00524 
00525               sd->oldcode = incode;
00526 
00527               if (sd->sp > sd->stack)
00528                      return *--sd->sp;
00529        }
00530        return code;
00531 }
00532 /* }}} */
00533 
00534 static int
00535 LWZReadByte(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, int *ZeroDataBlockP)
00536 {
00537        int rv;
00538 
00539  rv = LWZReadByte_(fd, sd, flag, input_code_size, ZeroDataBlockP);
00540  if (VERBOSE) printf("[LWZReadByte(,%d,%d) returning %d]\n",flag,input_code_size,rv);
00541        return(rv);
00542 }
00543 /* }}} */
00544 
00545 static void
00546 ReadImage(gdImagePtr im, gdIOCtx *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int *ZeroDataBlockP) /*1.4//, int ignore) */
00547 {
00548        unsigned char   c;
00549        int             v;
00550        int             xpos = 0, ypos = 0, pass = 0;
00551        int i;
00552        LZW_STATIC_DATA sd;
00553 
00554 
00555        /*
00556         **  Initialize the Compression routines
00557         */
00558        if (! ReadOK(fd,&c,1)) {
00559               return;
00560        }
00561 
00562        if (c > MAX_LWZ_BITS) {
00563               return;       
00564        }
00565 
00566        /* Stash the color map into the image */
00567        for (i=0; (i<gdMaxColors); i++) {
00568               im->red[i] = cmap[CM_RED][i];
00569               im->green[i] = cmap[CM_GREEN][i];
00570               im->blue[i] = cmap[CM_BLUE][i];
00571               im->open[i] = 1;
00572        }
00573        /* Many (perhaps most) of these colors will remain marked open. */
00574        im->colorsTotal = gdMaxColors;
00575        if (LWZReadByte(fd, &sd, TRUE, c, ZeroDataBlockP) < 0) {
00576               return;
00577        }
00578 
00579        /*
00580         **  If this is an "uninteresting picture" ignore it.
00581         **  REMOVED For 1.4
00582         */
00583        /*if (ignore) { */
00584        /*        while (LWZReadByte(fd, &sd, FALSE, c) >= 0) */
00585        /*                ; */
00586        /*        return; */
00587        /*} */
00588 
00589        while ((v = LWZReadByte(fd, &sd, FALSE, c, ZeroDataBlockP)) >= 0) {
00590               if (v >= gdMaxColors) {
00591                      v = 0;
00592               }
00593               /* This how we recognize which colors are actually used. */
00594               if (im->open[v]) {
00595                      im->open[v] = 0;
00596               }
00597               gdImageSetPixel(im, xpos, ypos, v);
00598               ++xpos;
00599               if (xpos == len) {
00600                      xpos = 0;
00601                      if (interlace) {
00602                             switch (pass) {
00603                                    case 0:
00604                                    case 1:
00605                                           ypos += 8; break;
00606                                    case 2:
00607                                           ypos += 4; break;
00608                                    case 3:
00609                                           ypos += 2; break;
00610                             }
00611 
00612                             if (ypos >= height) {
00613                                    ++pass;
00614                                    switch (pass) {
00615                                           case 1:
00616                                                  ypos = 4; break;
00617                                           case 2:
00618                                                  ypos = 2; break;
00619                                           case 3:
00620                                                  ypos = 1; break;
00621                                           default:
00622                                                  goto fini;
00623                                    }
00624                             }
00625                      } else {
00626                             ++ypos;
00627                      }
00628               }
00629               if (ypos >= height)
00630                      break;
00631        }
00632 
00633 fini:
00634        if (LWZReadByte(fd, &sd, FALSE, c, ZeroDataBlockP) >=0) {
00635               /* Ignore extra */
00636        }
00637 }
00638 /* }}} */