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(); } }
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; }
// 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; }
// 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; }
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; } }