Exemple #1
0
int EGifGCBToSavedExtension(const GraphicsControlBlock *GCB, 
			    GifFileType *GifFile, int ImageIndex)
{
    int i;
    size_t Len;
    GifByteType buf[sizeof(GraphicsControlBlock)]; /* a bit dodgy... */

    if (ImageIndex < 0 || ImageIndex > GifFile->ImageCount - 1)
	return GIF_ERROR;

    for (i = 0; i < GifFile->SavedImages[ImageIndex].ExtensionBlockCount; i++) {
	ExtensionBlock *ep = &GifFile->SavedImages[ImageIndex].ExtensionBlocks[i];
	if (ep->Function == GRAPHICS_EXT_FUNC_CODE) {
	    EGifGCBToExtension(GCB, ep->Bytes);
	    return GIF_OK;
	}
    }

    Len = EGifGCBToExtension(GCB, (GifByteType *)buf);
    if (GifAddExtensionBlock(&GifFile->SavedImages[ImageIndex].ExtensionBlockCount,
			     &GifFile->SavedImages[ImageIndex].ExtensionBlocks,
			     GRAPHICS_EXT_FUNC_CODE,
			     Len,
			     (unsigned char *)buf) == GIF_ERROR)
	return (GIF_ERROR);

    return (GIF_OK);
}
Exemple #2
0
/******************************************************************************
 This routine reads an entire GIF into core, hanging all its state info off
 the GifFileType pointer.  Call DGifOpenFileName() or DGifOpenFileHandle()
 first to initialize I/O.  Its inverse is EGifSpew().
*******************************************************************************/
int
DGifSlurp(GifFileType *GifFile)
{
    size_t ImageSize;
    GifRecordType RecordType;
    SavedImage *sp;
    GifByteType *ExtData;
    int ExtFunction;

    GifFile->ExtensionBlocks = NULL;
    GifFile->ExtensionBlockCount = 0;

    do {
        if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
            return (GIF_ERROR);

        switch (RecordType) {
          case IMAGE_DESC_RECORD_TYPE:
              if (DGifGetImageDesc(GifFile) == GIF_ERROR)
                  return (GIF_ERROR);

              sp = &GifFile->SavedImages[GifFile->ImageCount - 1];
              /* Allocate memory for the image */
              if (sp->ImageDesc.Width < 0 && sp->ImageDesc.Height < 0 &&
                      sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) {
                  return GIF_ERROR;
              }
              ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;

              if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) {
                  return GIF_ERROR;
              }
              sp->RasterBits = (unsigned char *)malloc(ImageSize *
                      sizeof(GifPixelType));

              if (sp->RasterBits == NULL) {
                  return GIF_ERROR;
              }

	      if (sp->ImageDesc.Interlace) {
		  int i, j;
		   /* 
		    * The way an interlaced image should be read - 
		    * offsets and jumps...
		    */
		  int InterlacedOffset[] = { 0, 4, 2, 1 };
		  int InterlacedJumps[] = { 8, 8, 4, 2 };
		  /* Need to perform 4 passes on the image */
		  for (i = 0; i < 4; i++)
		      for (j = InterlacedOffset[i]; 
			   j < sp->ImageDesc.Height;
			   j += InterlacedJumps[i]) {
			  if (DGifGetLine(GifFile, 
					  sp->RasterBits+j*sp->ImageDesc.Width, 
					  sp->ImageDesc.Width) == GIF_ERROR)
			      return GIF_ERROR;
		      }
	      }
	      else {
		  if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR)
		      return (GIF_ERROR);
	      }

              if (GifFile->ExtensionBlocks) {
                  sp->ExtensionBlocks = GifFile->ExtensionBlocks;
                  sp->ExtensionBlockCount = GifFile->ExtensionBlockCount;

                  GifFile->ExtensionBlocks = NULL;
                  GifFile->ExtensionBlockCount = 0;
              }
              break;

          case EXTENSION_RECORD_TYPE:
              if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR)
                  return (GIF_ERROR);
	      /* Create an extension block with our data */
	      if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
				       &GifFile->ExtensionBlocks, 
				       ExtFunction, ExtData[0], &ExtData[1])
		  == GIF_ERROR)
		  return (GIF_ERROR);
              while (ExtData != NULL) {
                  if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR)
                      return (GIF_ERROR);
                  /* Continue the extension block */
		  if (ExtData != NULL)
		      if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
					       &GifFile->ExtensionBlocks,
					       CONTINUE_EXT_FUNC_CODE, 
					       ExtData[0], &ExtData[1]) == GIF_ERROR)
                      return (GIF_ERROR);
              }
              break;

          case TERMINATE_RECORD_TYPE:
              break;

          default:    /* Should be trapped by DGifGetRecordType */
              break;
        }
    } while (RecordType != TERMINATE_RECORD_TYPE);

    return (GIF_OK);
}
Exemple #3
0
SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex) {
    // Use this as a container to hold information about any gif extension
    // blocks.  This generally stores transparency and animation instructions.
    SavedImage saveExt;
    SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt);
    saveExt.ExtensionBlocks = nullptr;
    saveExt.ExtensionBlockCount = 0;
    GifByteType* extData;
    int32_t extFunction;

    // We will loop over components of gif images until we find an image.  Once
    // we find an image, we will decode and return it.  While many gif files
    // contain more than one image, we will simply decode the first image.
    GifRecordType recordType;
    do {
        // Get the current record type
        if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) {
            return gif_error("DGifGetRecordType failed.\n", kInvalidInput);
        }
        switch (recordType) {
            case IMAGE_DESC_RECORD_TYPE: {
                *transIndex = find_trans_index(saveExt);

                // FIXME: Gif files may have multiple images stored in a single
                //        file.  This is most commonly used to enable
                //        animations.  Since we are leaving animated gifs as a
                //        TODO, we will return kSuccess after decoding the
                //        first image in the file.  This is the same behavior
                //        as SkImageDecoder_libgif.
                //
                //        Most times this works pretty well, but sometimes it
                //        doesn't.  For example, I have an animated test image
                //        where the first image in the file is 1x1, but the
                //        subsequent images are meaningful.  This currently
                //        displays the 1x1 image, which is not ideal.  Right
                //        now I am leaving this as an issue that will be
                //        addressed when we implement animated gifs.
                //
                //        It is also possible (not explicitly disallowed in the
                //        specification) that gif files provide multiple
                //        images in a single file that are all meant to be
                //        displayed in the same frame together.  I will
                //        currently leave this unimplemented until I find a
                //        test case that expects this behavior.
                return kSuccess;
            }
            // Extensions are used to specify special properties of the image
            // such as transparency or animation.
            case EXTENSION_RECORD_TYPE:
                // Read extension data
                if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData)) {
                    return gif_error("Could not get extension.\n", kIncompleteInput);
                }

                // Create an extension block with our data
                while (nullptr != extData) {
                    // Add a single block

#if GIFLIB_MAJOR < 5
                    if (AddExtensionBlock(&saveExt, extData[0],
                                          &extData[1]) == GIF_ERROR) {
#else
                    if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBlockCount,
                                                          &saveExt.ExtensionBlocks,
                                                          extFunction, extData[0], &extData[1])) {
#endif
                        return gif_error("Could not add extension block.\n", kIncompleteInput);
                    }
                    // Move to the next block
                    if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) {
                        return gif_error("Could not get next extension.\n", kIncompleteInput);
                    }
                }
                break;

            // Signals the end of the gif file
            case TERMINATE_RECORD_TYPE:
                break;

            default:
                // DGifGetRecordType returns an error if the record type does
                // not match one of the above cases.  This should not be
                // reached.
                SkASSERT(false);
                break;
        }
    } while (TERMINATE_RECORD_TYPE != recordType);

    return gif_error("Could not find any images to decode in gif file.\n", kInvalidInput);
}

bool SkGifCodec::GetDimensions(GifFileType* gif, SkISize* size, SkIRect* frameRect) {
    // Get the encoded dimension values
    SavedImage* image = &gif->SavedImages[gif->ImageCount - 1];
    const GifImageDesc& desc = image->ImageDesc;
    int frameLeft = desc.Left;
    int frameTop = desc.Top;
    int frameWidth = desc.Width;
    int frameHeight = desc.Height;
    int width = gif->SWidth;
    int height = gif->SHeight;

    // Ensure that the decode dimensions are large enough to contain the frame
    width = SkTMax(width, frameWidth + frameLeft);
    height = SkTMax(height, frameHeight + frameTop);

    // All of these dimensions should be positive, as they are encoded as unsigned 16-bit integers.
    // It is unclear why giflib casts them to ints.  We will go ahead and check that they are
    // in fact positive.
    if (frameLeft < 0 || frameTop < 0 || frameWidth < 0 || frameHeight < 0 || width <= 0 ||
            height <= 0) {
        return false;
    }

    frameRect->setXYWH(frameLeft, frameTop, frameWidth, frameHeight);
    size->set(width, height);
    return true;
}
Exemple #4
0
int
image_gif_load(image *im)
{
  int x, y, ofs;
  GifRecordType RecordType;
  GifPixelType *line = NULL;
  int ExtFunction = 0;
  GifByteType *ExtData;
  SavedImage *sp;
  SavedImage temp_save;
  int BackGround = 0;
  int trans_index = 0; // transparent index if any
  ColorMapObject *ColorMap;
  GifColorType *ColorMapEntry;

  temp_save.ExtensionBlocks = NULL;
  temp_save.ExtensionBlockCount = 0;

  // If reusing the object a second time, start over
  if (im->used) {
    DEBUG_TRACE("Recreating giflib objects\n");
    image_gif_finish(im);

    if (im->fh != NULL) {
      // reset file to begining of image
      PerlIO_seek(im->fh, im->image_offset, SEEK_SET);
    }
    else {
      // reset SV read
      im->sv_offset = im->image_offset;
    }

    buffer_clear(im->buf);

    image_gif_read_header(im);
  }

  do {
    if (DGifGetRecordType(im->gif, &RecordType) == GIF_ERROR) {
      warn("Image::Scale unable to read GIF file (%s)\n", SvPVX(im->path));
      image_gif_finish(im);
      return 0;
    }

    switch (RecordType) {
      case IMAGE_DESC_RECORD_TYPE:
        if (DGifGetImageDesc(im->gif) == GIF_ERROR) {
          warn("Image::Scale unable to read GIF file (%s)\n", SvPVX(im->path));
          image_gif_finish(im);
          return 0;
        }

        sp = &im->gif->SavedImages[im->gif->ImageCount - 1];

        im->width  = sp->ImageDesc.Width;
        im->height = sp->ImageDesc.Height;

        BackGround = im->gif->SBackGroundColor; // XXX needed?
        ColorMap = im->gif->Image.ColorMap ? im->gif->Image.ColorMap : im->gif->SColorMap;

        if (ColorMap == NULL) {
          warn("Image::Scale GIF image has no colormap (%s)\n", SvPVX(im->path));
          image_gif_finish(im);
          return 0;
        }

        // Allocate storage for decompressed image
        image_alloc(im, im->width, im->height);

        New(0, line, im->width, GifPixelType);

        if (im->gif->Image.Interlace) {
          int i;
          for (i = 0; i < 4; i++) {
            for (x = InterlacedOffset[i]; x < im->height; x += InterlacedJumps[i]) {
              ofs = x * im->width;
              if (DGifGetLine(im->gif, line, 0) != GIF_OK) {
                warn("Image::Scale unable to read GIF file (%s)\n", SvPVX(im->path));
                image_gif_finish(im);
                return 0;
              }

              for (y = 0; y < im->width; y++) {
                ColorMapEntry = &ColorMap->Colors[line[y]];
                im->pixbuf[ofs++] = COL_FULL(
                  ColorMapEntry->Red,
                  ColorMapEntry->Green,
                  ColorMapEntry->Blue,
                  trans_index == line[y] ? 0 : 255
                );
              }
            }
          }
        }
        else {
          ofs = 0;
          for (x = 0; x < im->height; x++) {
            if (DGifGetLine(im->gif, line, 0) != GIF_OK) {
              warn("Image::Scale unable to read GIF file (%s)\n", SvPVX(im->path));
              image_gif_finish(im);
              return 0;
            }

            for (y = 0; y < im->width; y++) {
              ColorMapEntry = &ColorMap->Colors[line[y]];
              im->pixbuf[ofs++] = COL_FULL(
                ColorMapEntry->Red,
                ColorMapEntry->Green,
                ColorMapEntry->Blue,
                trans_index == line[y] ? 0 : 255
              );
            }
          }
        }

        Safefree(line);
        break;

      case EXTENSION_RECORD_TYPE:
        if (DGifGetExtension(im->gif, &ExtFunction, &ExtData) == GIF_ERROR) {
          warn("Image::Scale unable to read GIF file (%s)\n", SvPVX(im->path));
          image_gif_finish(im);
          return 0;
        }

        if (ExtFunction == 0xF9) { // transparency info
          if (ExtData[1] & 1)
            trans_index = ExtData[4];
          else
            trans_index = -1;
          im->has_alpha = 1;
          DEBUG_TRACE("GIF transparency index: %d\n", trans_index);
        }

        while (ExtData != NULL) {
          /* Create an extension block with our data */
#ifdef GIFLIB_API_50
          if (GifAddExtensionBlock(&im->gif->ExtensionBlockCount, &im->gif->ExtensionBlocks, ExtFunction, ExtData[0], &ExtData[1]) == GIF_ERROR) {
#else
          temp_save.Function = ExtFunction;
          if (AddExtensionBlock(&temp_save, ExtData[0], &ExtData[1]) == GIF_ERROR) {
#endif
#ifdef GIFLIB_API_41
            PrintGifError();
#endif
            warn("Image::Scale unable to read GIF file (%s)\n", SvPVX(im->path));
            image_gif_finish(im);
            return 0;
          }

          if (DGifGetExtensionNext(im->gif, &ExtData) == GIF_ERROR) {
#ifdef GIFLIB_API_41
            PrintGifError();
#endif
            warn("Image::Scale unable to read GIF file (%s)\n", SvPVX(im->path));
            image_gif_finish(im);
            return 0;
          }

          ExtFunction = 0; // CONTINUE_EXT_FUNC_CODE
        }
        break;

      case TERMINATE_RECORD_TYPE:
      default:
        break;
    }
  } while (RecordType != TERMINATE_RECORD_TYPE);

  return 1;
}

void
image_gif_finish(image *im)
{
  if (im->gif != NULL) {
#ifdef GIFLIB_API_51
    if (DGifCloseFile(im->gif, NULL) != GIF_OK) {
#else
    if (DGifCloseFile(im->gif) != GIF_OK) {
#endif
#ifdef GIFLIB_API_41
      PrintGifError();
#endif
      warn("Image::Scale unable to close GIF file (%s)\n", SvPVX(im->path));
    }
    im->gif = NULL;

    DEBUG_TRACE("image_gif_finish\n");
  }
}
void EncodeToGifBufferWorker::Execute () {
    GifByteType
        * redBuff = (GifByteType *) _pixbuf,
        * greenBuff = (GifByteType *) _pixbuf + _width * _height,
        * blueBuff = (GifByteType *) _pixbuf + 2 * _width * _height,
        * alphaBuff = (GifByteType *) _pixbuf + 3 * _width * _height,
        * gifimgbuf = (GifByteType *) malloc(_width * _height * sizeof(GifByteType)); // the indexed image
    ColorMapObject *cmap;
    SavedImage * simg;

    if (NULL == gifimgbuf){
        SetErrorMessage("Out of memory");
        return;
    }

    cmap = GifMakeMapObject(_cmapSize, NULL);

    if (NULL == cmap){
        free(gifimgbuf);
        SetErrorMessage("Out of memory");
        return;
    }

    if (GIF_ERROR == GifQuantizeBuffer(
                _width, _height, &_colors,
               redBuff, greenBuff, blueBuff,
               gifimgbuf, cmap->Colors
        )){
        free(gifimgbuf);
        GifFreeMapObject(cmap);
        SetErrorMessage("Unable to quantize image");
        return;
    }

    int errcode;
    gifWriteCbData buffinf = {NULL, 0};
    GifFileType * gif;

    gif = EGifOpen((void *) &buffinf, gifWriteCB, &errcode);

    if (NULL == gif){
        free(gifimgbuf);
        GifFreeMapObject(cmap);
        SetErrorMessage(GifErrorString(errcode));
        return;
    }

    gif->SWidth = _width;
    gif->SHeight = _height;
    gif->SColorResolution = _cmapSize;

    simg = GifMakeSavedImage(gif, NULL);

    if (NULL == simg){
        free(gifimgbuf);
        EGifCloseFile(gif, &errcode); // will also free cmap
        SetErrorMessage("Out of memory");
        return;
    }

    simg->ImageDesc.Left = 0;
    simg->ImageDesc.Top = 0;
    simg->ImageDesc.Width = _width;
    simg->ImageDesc.Height = _height;
    simg->ImageDesc.Interlace = _interlaced;
    simg->ImageDesc.ColorMap = cmap;
    simg->RasterBits = gifimgbuf;

    // for some reason giflib sometimes creates an invalid file if the global
    // color table is not set as well
    gif->SColorMap = cmap;

    if (_trans){
        ExtensionBlock ext;
        // 1. assign transparent color index in color table
        GraphicsControlBlock gcb = {0, false, 0, _colors++};
        // 2. replace transparent pixels above threshold with this color
        remapTransparentPixels(gifimgbuf, alphaBuff, _width, _height, gcb.TransparentColor, _threshold);
        // 3. create a control block
        size_t extlen = EGifGCBToExtension(&gcb, (GifByteType *) &ext);
        if (GIF_ERROR == GifAddExtensionBlock(
                &(simg->ExtensionBlockCount),
                &(simg->ExtensionBlocks),
                GRAPHICS_EXT_FUNC_CODE,
                extlen,
                (unsigned char *) &ext)
            ) {
            EGifCloseFile(gif, &errcode);
            SetErrorMessage("Out of memory");
            return;
        }
    }


    if (GIF_ERROR == EGifSpew(gif)){
        EGifCloseFile(gif, &errcode);
        SetErrorMessage(GifErrorString(gif->Error));
        return;
    }

    _gifbuf = (char *) buffinf.buff;
    _gifbufsize = buffinf.buffsize;

    return;
}
Exemple #6
0
SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* transIndex) {
    // Use this as a container to hold information about any gif extension
    // blocks.  This generally stores transparency and animation instructions.
    SavedImage saveExt;
    SkAutoTCallVProc<SavedImage, FreeExtension> autoFreeExt(&saveExt);
    saveExt.ExtensionBlocks = nullptr;
    saveExt.ExtensionBlockCount = 0;
    GifByteType* extData;
    int32_t extFunction;

    // We will loop over components of gif images until we find an image.  Once
    // we find an image, we will decode and return it.  While many gif files
    // contain more than one image, we will simply decode the first image.
    GifRecordType recordType;
    do {
        // Get the current record type
        if (GIF_ERROR == DGifGetRecordType(gif, &recordType)) {
            return gif_error("DGifGetRecordType failed.\n", kInvalidInput);
        }
        switch (recordType) {
            case IMAGE_DESC_RECORD_TYPE: {
                *transIndex = find_trans_index(saveExt);

                // FIXME: Gif files may have multiple images stored in a single
                //        file.  This is most commonly used to enable
                //        animations.  Since we are leaving animated gifs as a
                //        TODO, we will return kSuccess after decoding the
                //        first image in the file.  This is the same behavior
                //        as SkImageDecoder_libgif.
                //
                //        Most times this works pretty well, but sometimes it
                //        doesn't.  For example, I have an animated test image
                //        where the first image in the file is 1x1, but the
                //        subsequent images are meaningful.  This currently
                //        displays the 1x1 image, which is not ideal.  Right
                //        now I am leaving this as an issue that will be
                //        addressed when we implement animated gifs.
                //
                //        It is also possible (not explicitly disallowed in the
                //        specification) that gif files provide multiple
                //        images in a single file that are all meant to be
                //        displayed in the same frame together.  I will
                //        currently leave this unimplemented until I find a
                //        test case that expects this behavior.
                return kSuccess;
            }
            // Extensions are used to specify special properties of the image
            // such as transparency or animation.
            case EXTENSION_RECORD_TYPE:
                // Read extension data
                if (GIF_ERROR == DGifGetExtension(gif, &extFunction, &extData)) {
                    return gif_error("Could not get extension.\n", kIncompleteInput);
                }

                // Create an extension block with our data
                while (nullptr != extData) {
                    // Add a single block
                    if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBlockCount,
                                                          &saveExt.ExtensionBlocks,
                                                          extFunction, extData[0], &extData[1]))
                    {
                        return gif_error("Could not add extension block.\n", kIncompleteInput);
                    }
                    // Move to the next block
                    if (GIF_ERROR == DGifGetExtensionNext(gif, &extData)) {
                        return gif_error("Could not get next extension.\n", kIncompleteInput);
                    }
                }
                break;

            // Signals the end of the gif file
            case TERMINATE_RECORD_TYPE:
                break;

            default:
                // DGifGetRecordType returns an error if the record type does
                // not match one of the above cases.  This should not be
                // reached.
                SkASSERT(false);
                break;
        }
    } while (TERMINATE_RECORD_TYPE != recordType);

    return gif_error("Could not find any images to decode in gif file.\n", kInvalidInput);
}
Exemple #7
0
int
DGifSlurp(GifFileType *GifFile)
{
    size_t ImageSize;
    GifRecordType RecordType;
    SavedImage *sp;
    GifByteType *ExtData;
    int ExtFunction;

    GifFile->ExtensionBlocks = NULL;
    GifFile->ExtensionBlockCount = 0;

    //主循环
    do {

        //判断当前读取的块类型
        if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
            return (GIF_ERROR);

        switch (RecordType) {

            //每一帧图片的图片头 Descriptor 也就是0x2c
            //包含当前帧的上下左右位置(为了节省空间,GIF的单个帧可以不是完整的GIF的大小,如果小于GIF的分辨率 就需要上下左右来定位)
            //局部的颜色空间(每一帧可以有自己的颜色空间,是可选的)
          case IMAGE_DESC_RECORD_TYPE:
              if (DGifGetImageDesc(GifFile) == GIF_ERROR)
                  return (GIF_ERROR);

              sp = &GifFile->SavedImages[GifFile->ImageCount - 1];
              /* Allocate memory for the image */
              if (sp->ImageDesc.Width < 0 && sp->ImageDesc.Height < 0 &&
                      sp->ImageDesc.Width > (INT_MAX / sp->ImageDesc.Height)) {
                  return GIF_ERROR;
              }
              ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height;

              if (ImageSize > (SIZE_MAX / sizeof(GifPixelType))) {
                  return GIF_ERROR;
              }

              //分配空间给解码后的图像像素
              sp->RasterBits = (unsigned char *)malloc(ImageSize *
                      sizeof(GifPixelType));

              if (sp->RasterBits == NULL) {
                  return GIF_ERROR;
              }

	      if (sp->ImageDesc.Interlace) {
		  int i, j;
		   /* 
		    * The way an interlaced image should be read - 
		    * offsets and jumps...
		    */
		  int InterlacedOffset[] = { 0, 4, 2, 1 };
		  int InterlacedJumps[] = { 8, 8, 4, 2 };
		  /* Need to perform 4 passes on the image */
		  for (i = 0; i < 4; i++)
		      for (j = InterlacedOffset[i]; 
			   j < sp->ImageDesc.Height;
			   j += InterlacedJumps[i]) {
			  if (DGifGetLine(GifFile, 
					  sp->RasterBits+j*sp->ImageDesc.Width, 
					  sp->ImageDesc.Width) == GIF_ERROR)
			      return GIF_ERROR;
		      }
	      }
	      else {
		  if (DGifGetLine(GifFile,sp->RasterBits,ImageSize)==GIF_ERROR)
		      return (GIF_ERROR);
	      }

              if (GifFile->ExtensionBlocks) {
                  sp->ExtensionBlocks = GifFile->ExtensionBlocks;
                  sp->ExtensionBlockCount = GifFile->ExtensionBlockCount;

                  GifFile->ExtensionBlocks = NULL;
                  GifFile->ExtensionBlockCount = 0;
              }
              break;

          case EXTENSION_RECORD_TYPE:
              if (DGifGetExtension(GifFile,&ExtFunction,&ExtData) == GIF_ERROR)
                  return (GIF_ERROR);
	      /* Create an extension block with our data */
              if (ExtData != NULL) {
		  if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
					   &GifFile->ExtensionBlocks, 
					   ExtFunction, ExtData[0], &ExtData[1])
		      == GIF_ERROR)
		      return (GIF_ERROR);
	      }
              while (ExtData != NULL) {
                  if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR)
                      return (GIF_ERROR);
                  /* Continue the extension block */
		  if (ExtData != NULL)
		      if (GifAddExtensionBlock(&GifFile->ExtensionBlockCount,
					       &GifFile->ExtensionBlocks,
					       CONTINUE_EXT_FUNC_CODE, 
					       ExtData[0], &ExtData[1]) == GIF_ERROR)
                      return (GIF_ERROR);
              }
              break;

          case TERMINATE_RECORD_TYPE:
              break;

          default:    /* Should be trapped by DGifGetRecordType */
              break;
        }

        //当类型为Terminate也就是0x3b的时候 表示已经读到GIF文件尾 退出循环
    } while (RecordType != TERMINATE_RECORD_TYPE);

    return (GIF_OK);
}