GifPixRect::GifPixRect(GifFileType *gifFile, int i)
: PixRect(theApp.m_device, PIXRECT_PLAINSURFACE, getImageSize(gifFile, i)) {
  const SavedImage &image = gifFile->SavedImages[i];
  copyRasterBits(image.RasterBits, image.ImageDesc.ColorMap ? image.ImageDesc.ColorMap->Colors : gifFile->SColorMap->Colors);
  m_topLeft = CPoint(image.ImageDesc.Left, image.ImageDesc.Top);

  if(DGifSavedExtensionToGCB(gifFile, i, &m_gcb) != GIF_OK) {
    m_gcb = getDefaultGCB();
  }
}
Exemple #2
0
bool MCGIFImageLoader::LoadFrames(MCBitmapFrame *&r_frames, uint32_t &r_count)
{
	bool t_success;
	t_success = true;
	
	MCImageBitmap *t_canvas;
	t_canvas = nil;
	
	// restoration info
	MCImageBitmap *t_restore_image = nil;
	int t_disposal_mode = DISPOSAL_UNSPECIFIED;
	MCRectangle t_disposal_region = kMCEmptyRectangle;

	// The list of frames.
	MCBitmapFrame *t_frames = nil;

	t_success = GIF_OK == DGifSlurp(m_gif);

	// Fetch the width and height of the virtual canvas.
	int32_t t_width, t_height;

	if (t_success)
	{
		t_width = m_gif -> SWidth;
		t_height = m_gif -> SHeight;

		// create the canvas image
		t_success = MCImageBitmapCreate(t_width, t_height, t_canvas);
	}

	// The current frame count. The number of frames is the same as the
	// number of images in the GIF.
	uint32_t t_frame_count;
	t_frame_count = 0;
	
	// If true, the new image will be merged with the old - otherwise the mask
	// replaces it.
	bool t_overlay;
	t_overlay = false;
	
	// Loop through all the images, making frames as we go.
	for(uindex_t i = 0; t_success && i < m_gif -> ImageCount; i++)
	{
		// Process the disposal.
		switch (t_disposal_mode)
		{
		case DISPOSE_BACKGROUND:
			gif_fill_image_region(t_canvas, t_disposal_region, 0);
			break;

		case DISPOSE_PREVIOUS:
			if (t_restore_image != nil)
				gif_paste_image(t_canvas, t_restore_image, t_disposal_region.x, t_disposal_region.y);
			break;

		case DISPOSE_DO_NOT:
			t_overlay = true;
			break;

		default:
			t_overlay = false;
			break;
		}

		// Fetch the image information.
		GraphicsControlBlock t_image_gcb;
		MCRectangle t_image_region;
		ColorMapObject *t_image_colors = nil;
		int32_t t_image_transparency;
		int32_t t_image_delay;
		int32_t t_image_disposal;
		GifByteType *t_image_raster;

		// First the information from the image description.
		t_image_region.x = m_gif -> SavedImages[i] . ImageDesc . Left;
		t_image_region.y = m_gif -> SavedImages[i] . ImageDesc . Top;
		t_image_region.width = m_gif -> SavedImages[i] . ImageDesc . Width;
		t_image_region.height = m_gif -> SavedImages[i] . ImageDesc . Height;
		t_image_colors = m_gif -> SavedImages[i] . ImageDesc . ColorMap;
		t_image_raster = m_gif -> SavedImages[i] . RasterBits;
		if (t_image_colors == nil)
			t_image_colors = m_gif -> SColorMap;
		
		// Then the information from the GCB.
		if (GIF_OK == DGifSavedExtensionToGCB(m_gif, i, &t_image_gcb))
		{
			t_image_transparency = t_image_gcb . TransparentColor;
			t_image_delay = t_image_gcb . DelayTime;
			t_image_disposal = t_image_gcb . DisposalMode;
		}
		else
		{
			t_image_transparency = -1;
			t_image_delay = 0;
			t_image_disposal = DISPOSAL_UNSPECIFIED;
			}
			
		// If disposal is 'previous' then cache the portion of the canvas we are
		// about to affect.
		if (t_image_disposal == DISPOSE_PREVIOUS)
			{
			if (t_restore_image != nil)
			{
				if (t_disposal_mode != DISPOSE_PREVIOUS ||
					!MCU_equal_rect(t_disposal_region, t_image_region))
				{
					MCImageFreeBitmap(t_restore_image);
					t_restore_image = nil;
				}
			}
			if (t_restore_image == nil)
				t_success = MCImageCopyBitmapRegion(t_canvas, t_image_region, t_restore_image);
		}
		
		if (t_success)
		{
			// Render the image into the canvas.
			gif_draw_image_into_canvas(t_canvas, t_image_raster,
				t_image_region.x, t_image_region.y, t_image_region.width, t_image_region.height,
				t_image_colors, t_image_transparency, t_overlay);
			
			// Generate our frame.
			t_success = MCMemoryResizeArray(t_frame_count + 1, t_frames, t_frame_count);
		}

		MCImageBitmap *t_frame_bitmap = nil;
		if (t_success)
			t_success = MCImageCopyBitmap(t_canvas, t_frame_bitmap);
			
		if (t_success)
		{
			MCImageBitmapCheckTransparency(t_frame_bitmap);
			t_frames[t_frame_count - 1].image = t_frame_bitmap;
			t_frames[t_frame_count - 1].duration = t_image_delay * 10; // convert 1/100 seconds to milliseconds
			t_frames[t_frame_count - 1].x_scale = t_frames[t_frame_count - 1].y_scale = 1.0;
		}

		t_disposal_region = t_image_region;
		t_disposal_mode = t_image_disposal;
	}
	
	MCImageFreeBitmap(t_canvas);
	MCImageFreeBitmap(t_restore_image);
	
	if (t_success)
	{
		r_frames = t_frames;
		r_count = t_frame_count;
	}
	else
		MCImageFreeFrames(t_frames, t_frame_count);

	return t_success;
}
Exemple #3
0
// Read animated GIF bitstream from 'filename' into 'AnimatedImage' struct.
static int ReadAnimatedGIF(const char filename[], AnimatedImage* const image,
                           int dump_frames, const char dump_folder[]) {
  uint32_t frame_count;
  uint32_t canvas_width, canvas_height;
  uint32_t i;
  int gif_error;
  GifFileType* gif;

  gif = DGifOpenFileName(filename, NULL);
  if (gif == NULL) {
    fprintf(stderr, "Could not read file: %s.\n", filename);
    return 0;
  }

  gif_error = DGifSlurp(gif);
  if (gif_error != GIF_OK) {
    fprintf(stderr, "Could not parse image: %s.\n", filename);
    GIFDisplayError(gif, gif_error);
    DGifCloseFile(gif, NULL);
    return 0;
  }

  // Animation properties.
  image->canvas_width = (uint32_t)gif->SWidth;
  image->canvas_height = (uint32_t)gif->SHeight;
  if (image->canvas_width > MAX_CANVAS_SIZE ||
      image->canvas_height > MAX_CANVAS_SIZE) {
    fprintf(stderr, "Invalid canvas dimension: %d x %d\n",
            image->canvas_width, image->canvas_height);
    DGifCloseFile(gif, NULL);
    return 0;
  }
  image->loop_count = GetLoopCountGIF(gif);
  image->bgcolor = GetBackgroundColorGIF(gif);

  frame_count = (uint32_t)gif->ImageCount;
  if (frame_count == 0) {
    DGifCloseFile(gif, NULL);
    return 0;
  }

  if (image->canvas_width == 0 || image->canvas_height == 0) {
    image->canvas_width = gif->SavedImages[0].ImageDesc.Width;
    image->canvas_height = gif->SavedImages[0].ImageDesc.Height;
    gif->SavedImages[0].ImageDesc.Left = 0;
    gif->SavedImages[0].ImageDesc.Top = 0;
    if (image->canvas_width == 0 || image->canvas_height == 0) {
      fprintf(stderr, "Invalid canvas size in GIF.\n");
      DGifCloseFile(gif, NULL);
      return 0;
    }
  }
  // Allocate frames.
  AllocateFrames(image, frame_count);

  canvas_width = image->canvas_width;
  canvas_height = image->canvas_height;

  // Decode and reconstruct frames.
  for (i = 0; i < frame_count; ++i) {
    const int canvas_width_in_bytes = canvas_width * kNumChannels;
    const SavedImage* const curr_gif_image = &gif->SavedImages[i];
    GraphicsControlBlock curr_gcb;
    DecodedFrame* curr_frame;
    uint8_t* curr_rgba;

    memset(&curr_gcb, 0, sizeof(curr_gcb));
    DGifSavedExtensionToGCB(gif, i, &curr_gcb);

    curr_frame = &image->frames[i];
    curr_rgba = curr_frame->rgba;
    curr_frame->duration = GetFrameDurationGIF(gif, i);

    if (i == 0) {  // Initialize as transparent.
      curr_frame->is_key_frame = 1;
      ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
    } else {
      DecodedFrame* const prev_frame = &image->frames[i - 1];
      const GifImageDesc* const prev_desc = &gif->SavedImages[i - 1].ImageDesc;
      GraphicsControlBlock prev_gcb;
      memset(&prev_gcb, 0, sizeof(prev_gcb));
      DGifSavedExtensionToGCB(gif, i - 1, &prev_gcb);

      curr_frame->is_key_frame =
          IsKeyFrameGIF(prev_desc, prev_gcb.DisposalMode, prev_frame,
                        canvas_width, canvas_height);

      if (curr_frame->is_key_frame) {  // Initialize as transparent.
        ZeroFillCanvas(curr_rgba, canvas_width, canvas_height);
      } else {
        int prev_frame_disposed, curr_frame_opaque;
        int prev_frame_completely_covered;
        // Initialize with previous canvas.
        uint8_t* const prev_rgba = image->frames[i - 1].rgba;
        CopyCanvas(prev_rgba, curr_rgba, canvas_width, canvas_height);

        // Dispose previous frame rectangle.
        prev_frame_disposed =
            (prev_gcb.DisposalMode == DISPOSE_BACKGROUND ||
             prev_gcb.DisposalMode == DISPOSE_PREVIOUS);
        curr_frame_opaque =
            (curr_gcb.TransparentColor == NO_TRANSPARENT_COLOR);
        prev_frame_completely_covered =
            curr_frame_opaque &&
            CoversFrameGIF(&curr_gif_image->ImageDesc, prev_desc);

        if (prev_frame_disposed && !prev_frame_completely_covered) {
          switch (prev_gcb.DisposalMode) {
            case DISPOSE_BACKGROUND: {
              ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
                                prev_desc->Left, prev_desc->Top,
                                prev_desc->Width, prev_desc->Height);
              break;
            }
            case DISPOSE_PREVIOUS: {
              int src_frame_num = i - 2;
              while (src_frame_num >= 0) {
                GraphicsControlBlock src_frame_gcb;
                memset(&src_frame_gcb, 0, sizeof(src_frame_gcb));
                DGifSavedExtensionToGCB(gif, src_frame_num, &src_frame_gcb);
                if (src_frame_gcb.DisposalMode != DISPOSE_PREVIOUS) break;
                --src_frame_num;
              }
              if (src_frame_num >= 0) {
                // Restore pixels inside previous frame rectangle to
                // corresponding pixels in source canvas.
                uint8_t* const src_frame_rgba =
                    image->frames[src_frame_num].rgba;
                CopyFrameRectangle(src_frame_rgba, curr_rgba,
                                   canvas_width_in_bytes,
                                   prev_desc->Left, prev_desc->Top,
                                   prev_desc->Width, prev_desc->Height);
              } else {
                // Source canvas doesn't exist. So clear previous frame
                // rectangle to background.
                ZeroFillFrameRect(curr_rgba, canvas_width_in_bytes,
                                  prev_desc->Left, prev_desc->Top,
                                  prev_desc->Width, prev_desc->Height);
              }
              break;
            }
            default:
              break;  // Nothing to do.
          }
        }
      }
    }

    // Decode current frame.
    if (!ReadFrameGIF(curr_gif_image, gif->SColorMap, curr_gcb.TransparentColor,
                      canvas_width_in_bytes, curr_rgba)) {
      DGifCloseFile(gif, NULL);
      return 0;
    }

    if (dump_frames) {
      if (!DumpFrame(filename, dump_folder, i, curr_rgba,
                     canvas_width, canvas_height)) {
        DGifCloseFile(gif, NULL);
        return 0;
      }
    }
  }
  DGifCloseFile(gif, NULL);
  return 1;
}
Exemple #4
0
// Get duration of 'n'th frame in milliseconds.
static int GetFrameDurationGIF(GifFileType* gif, int n) {
  GraphicsControlBlock gcb;
  memset(&gcb, 0, sizeof(gcb));
  DGifSavedExtensionToGCB(gif, n, &gcb);
  return gcb.DelayTime * 10;
}
Exemple #5
0
static int GetTransparentIndexGIF(GifFileType* gif) {
  GraphicsControlBlock first_gcb;
  memset(&first_gcb, 0, sizeof(first_gcb));
  DGifSavedExtensionToGCB(gif, 0, &first_gcb);
  return first_gcb.TransparentColor;
}
void AnimatedImage::extractGifData(const GifFileType *gifFile) {
  PixelAccessor *pa    = NULL;
  GifFrame      *frame = NULL;
  try {
    m_size.cx = gifFile->SWidth;
    m_size.cy = gifFile->SHeight;
    m_workPr  = new PixRect(m_device, PIXRECT_TEXTURE, m_size); TRACE_NEW(m_workPr);

    const int           imageCount   = gifFile->ImageCount;
    ColorMapObject     *gfColorMap   = gifFile->SColorMap;
    const GifColorType &colorType    = gfColorMap->Colors[gifFile->SBackGroundColor];
    const int           bitsPerPixel = gfColorMap->BitsPerPixel;

    m_backgroundColor      = D3DCOLOR_RGBA(colorType.Red, colorType.Green, colorType.Blue,255);

    for(int k = 0; k < gifFile->ExtensionBlockCount; k++) {
      const ExtensionBlock &block = gifFile->ExtensionBlocks[k];
      switch(block.Function) {
      case COMMENT_EXT_FUNC_CODE:
        m_comment += format(_T("%*.*s"), block.ByteCount, block.ByteCount, block.Bytes);
        break;
      case APPLICATION_EXT_FUNC_CODE:
        parseApplicationBlock(block.Bytes, block.ByteCount);
        break;
      }
    }
    m_frameTable.setCapacity(imageCount);
    for(int i = 0; i < imageCount; i++) {
      SavedImage &image = gifFile->SavedImages[i];
      m_frameTable.add(GifFrame());
      frame = &m_frameTable.last();

      String imageComment;
      for(int k = 0; k < image.ExtensionBlockCount; k++) {
        const ExtensionBlock &block = image.ExtensionBlocks[k];
        switch(block.Function) {
        case COMMENT_EXT_FUNC_CODE:
          imageComment += format(_T("%*.*s"), block.ByteCount, block.ByteCount, block.Bytes);
          break;

        case APPLICATION_EXT_FUNC_CODE:
          parseApplicationBlock(block.Bytes, block.ByteCount);
          break;
        }
      }
      if(imageComment.length() > 0) {
        m_comment += _T("\n") + imageComment;
      }

      frame->m_owner        = this;
      frame->m_rect.left    = image.ImageDesc.Left;
      frame->m_rect.top     = image.ImageDesc.Top;
      frame->m_rect.right   = image.ImageDesc.Left + image.ImageDesc.Width;
      frame->m_rect.bottom  = image.ImageDesc.Top  + image.ImageDesc.Height;
      frame->m_srcRect      = CRect(0,0, image.ImageDesc.Width, image.ImageDesc.Height);

      int transparentColor = NO_TRANSPARENT_COLOR;

      GraphicsControlBlock gcb;
      if(DGifSavedExtensionToGCB((GifFileType*)gifFile, i, &gcb) == GIF_OK) {
        frame->m_disposalMode = gcb.DisposalMode;
        frame->m_delayTime    = max(60,10*gcb.DelayTime);
        if((transparentColor = gcb.TransparentColor) != NO_TRANSPARENT_COLOR) {
          frame->m_useTransparency = true;
        }
      }

      const CSize sz = frame->m_rect.Size();
      frame->m_pr = new PixRect(m_device, PIXRECT_TEXTURE, sz, D3DPOOL_SYSTEMMEM, D3DFMT_A8R8G8B8); TRACE_NEW(frame->m_pr);

      const ColorMapObject *colorMap = image.ImageDesc.ColorMap ? image.ImageDesc.ColorMap : gfColorMap;
      const GifColorType   *colors   = colorMap->Colors;
      const GifPixelType   *pixelp   = image.RasterBits;

      pa = frame->m_pr->getPixelAccessor(0 /*D3DLOCK_NOSYSLOCK | D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_NOOVERWRITE*/);
      for(CPoint p(0,0); p.y < sz.cy; p.y++) {
        for(p.x = 0; p.x < sz.cx; p.x++) {
          const int entry = *(pixelp++);
          if((entry == transparentColor) && frame->m_useTransparency) {
            pa->setPixel(p, D3DCOLOR_RGBA(0,0,0,0));
          } else {
            const GifColorType &mapEntry = colors[entry];
            pa->setPixel(p, D3DCOLOR_RGBA(mapEntry.Red, mapEntry.Green, mapEntry.Blue, 255));
          }
        }
      }
      frame->m_pr->releasePixelAccessor();
      pa = NULL;
//      frame.m_pr->moveToPool(D3DPOOL_DEFAULT);
    }
  } catch(...) {
    if(pa) {
      frame->m_pr->releasePixelAccessor();
    }
    unload();
    throw;
  }
}