【问题标题】:How to extract frames from this GIF image? (Access violation in TGIFRenderer.Draw)如何从此 GIF 图像中提取帧? (TGIFRenderer.Draw 中的访问冲突)
【发布时间】:2016-04-06 07:15:47
【问题描述】:

我有这个从动画 GIF 中提取帧的函数。它适用于除此之外的所有 GIF:

{ Loads a GIF. Returns a list of BMP frames }
function GetGifFrames(GifFile: string): TObjectList;
var
  GIF: TGIFImage;
  TempFrame: TBitmap; 
  Frame: TBitmap;
  Counter: Integer;
  GR: TGIFRenderer;
begin
 { Load GIF }
 GIF := TGIFImage.Create;
 TRY
  TRY
   Gif.Animate := FALSE;
   GIF.LoadFromFile(GifFile);
  EXCEPT
    MesajError('Cannot load '+ GifFile);
    EXIT(NIL);
  END;

  if Gif.Images.Count= 1 then
   begin
     MsgError('This is not an animated GIF'+ CRLF+ GifFile);
     EXIT(NIL);
   end;

  Result:= TObjectList.Create;
  Result.OwnsObjects:= TRUE;                                   { Array of images }

  { GIF render }
  TempFrame:= TBitmap.Create;
  GR:= TGIFRenderer.Create(GIF);                               { GIF render }
  TRY
   TempFrame.SetSize(GIF.Width, GIF.Height);

   for Counter:= 0 to GIF.Images.Count-1 DO
    begin
      { Skip bad frames }
      if GIF.Images[Counter].Empty
      then Continue;

      { Create new frame }
      Frame:= TBitmap.Create;
      Frame.SetSize(GIF.Width, GIF.Height);
      GR.Draw(TempFrame.Canvas, TempFrame.Canvas.ClipRect);   <---------- AV here   { Transfer image from GIF to BMP }     
      Frame.Assign(TempFrame);
      Result.Add(Frame);                                       { Add to list of bitmap frames }
      GR.NextFrame;                                            { Advance }
    end;

  FINALLY
   FreeAndNil(GR);
   FreeAndNil(TempFrame);
  END;

  FINALLY
   FreeAndNil(GIF);
 END;
end;

我在上面指示的行上有一个 AV

调试器异常通知项目 Tester.exe 引发异常 类 $C0000005 与消息“访问冲突在 0x005d3924:读取 地址 0x0000002c'。

更新: 可编译测试器herehere

堆栈跟踪是:

   GetGifFrames('C:\Test gif\err.gif')
   GIFImg.TGIFRenderer.Draw($7EFA9070,(0, 0, 108, 146, (0, 0), (108, 146)))
   GIFImg.TGIFRenderer.GetBitmap
   GIFImg.TGIFRenderer.RenderFrame

在渲染框架中,它在这一行崩溃:

PreviousBuffer.Canvas.CopyRect(PreviousBuffer.Canvas.ClipRect, Buffer.Canvas, Buffer.Canvas.ClipRect);

这是因为 PreviousBuffer 为 NIL!!!!

如何解决这个问题?

【问题讨论】:

  • 当您在GR.Draw(TempFrame.Canvas, TempFrame.Canvas.ClipRect); 行设置断点时,TempFrame 中的GR 中存储了什么值?
  • @ZamronyP.Juhara - TempFrame.Width 和 Height 的值没问题(gif 大小为 108x146)。 ClipRect 也可以。也许这个特定的 GIF 格式不正确?
  • 为什么显示完整的错误信息这么难,你不觉得可能有一些有价值的信息吗?顺便说一句,我用图像(“Sansanimated.gif”)测试了你的代码,但没有 AV,也没有任何其他错误,TObjectList 中有 13 帧。
  • 读取地址 0x0000002C 看起来像访问 nil 对象引用,并且由于您只访问 TempFrame 及其 Canvas,要么是 nil 吗?当然在你的代码中他们不应该,但是......
  • @TomBrunberg-嗨,汤姆。 1. 测试仪(DPR、PAS、DFM 文件)在这里可用:s000.tinyupload.com/… 还有编译文件(exe)将显示 AV(以防您仍然无法在您的计算机中重现)。 2. 我认为当您尝试访问 nil obj 时,您会得到“读取地址 00000000”。

标签: delphi graphics gif delphi-xe7


【解决方案1】:

GIF 文件中帧的属性之一是DisposalMethod,它定义了应如何处理帧中的图像以准备下一帧。在原始文件(帖子顶部的“Sansanimated.gif”链接)中,所有 13 帧都设置为 dmNoDisposal。这在您的代码中可以正常工作,没有问题。在文件“err.gif”中,两个帧都有dmPrevious,这需要一个额外的内部位图。如果GIFRenderer 未初始化为动画,则不会分配此位图。

要为具有dmPrevious 处理方法设置的帧正确初始化 GIFRenderer,请添加一行

GR.Animate := True;  // <---- add this line

在创建 GR 之后。

【讨论】:

  • 出于好奇:是什么让代码适用于所有其他 GIF,但不适用于这个?
  • 也许省略Gif.Animate := FALSE; 会有同样的效果。
  • @kobik- 抱歉。我不明白
  • @SolarWind,您在代码中明确添加了Gif.Animate := FALSE;(我不知道您为什么这样做)您是否尝试删除此行?
  • @SolarWind 谢谢!有效的 gif 没有将处理模式设置为 dmPreviousdmPrevious 主要用于仅更新每帧中总图像的较小部分的 gif。我的电脑上没有这样的 gif。
猜你喜欢
  • 2010-10-31
  • 2014-12-22
  • 2014-03-26
  • 1970-01-01
  • 2019-08-18
  • 1970-01-01
  • 2012-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多