如何从这个GIF图像中提取帧?(TGIFRenderer.Draw中出现访问冲突)

4

我有一个从动态GIF中提取帧的函数。它适用于所有GIF,除了这个:enter image description here

{ 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记录。

Debugger Exception Notification Project Tester.exe引发异常类$C0000005,错误信息为“访问地址0x0000002c处读取数据时遇到访问冲突”,具体内容如下:

更新: 可编译的测试程序下载地址:这里这里

调用栈跟踪:

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

在Render frame中,它会在这一行崩溃:

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

这是因为PreviousBuffer为NIL!!!
如何修复?

当您在GR.Draw(TempFrame.Canvas, TempFrame.Canvas.ClipRect);行上设置断点时,TempFrame中的GR存储了什么值? - Zamrony P. Juhara
@ZamronyP.Juhara - TempFrame.Width和Height的值是正确的(gif的大小为108x146)。ClipRect也没问题。也许这个特定的GIF文件格式有问题? - Gabriel
1
为什么显示完整的错误信息如此困难,您不认为可能会有一些有价值的信息吗?顺便说一下,我使用图像(“Sansanimated.gif”)测试了您的代码,但没有AV或任何其他错误,在TObjectList中有13个帧。 - Tom Brunberg
读取地址0x0000002C似乎是对空对象引用的访问,因为您只访问 TempFrame 及其 Canvas。它们可能为空吗?当然,在您的代码中它们不应该为空,但是... - Tom Brunberg
@TomBrunberg- 嗨Tom。1. 这里提供测试员(DPR,PAS,DFM文件)下载链接:http://s000.tinyupload.com/download.php?file_id=87092305545494126013&t=8709230554549412601366009,还有编译的文件(exe),将显示AV(如果您仍然无法在计算机上重现)。2. 当您尝试访问nil对象时,我认为您会得到“读取00000000地址”的错误提示。 - Gabriel
@TomBrunberg-抱歉,是我错了。那张图片确实可以使用。我把它替换成了“损坏”的图片。而且,是的,“损坏”的gif在所有其他程序中都可以使用(包括IrfanView)。 - Gabriel
1个回答

2
在GIF文件中,帧的一个属性是DisposalMethod,它定义了帧中的图像在准备下一帧时应如何处理。在原始文件(本贴顶部的"Sansanimated.gif"链接)中,所有13帧都设置为dmNoDisposal。在你的代码中,这样可以正常工作。在"err.gif"文件中,两个帧都具有dmPrevious,需要额外的内部位图。如果GIFRenderer未被初始化为动画,则不会分配此位图。
要使GIFRenderer针对具有dmPrevious处置方法设置正确初始化,请添加以下一行。
GR.Animate := True;  // <---- add this line

在创建GR之后立即进行。

出于好奇:是什么让这段代码适用于所有其他 GIF,但对于这个不起作用? - Gabriel
也许省略 Gif.Animate := FALSE; 会有相同的效果。 - kobik
@kobik- 抱歉,我不明白。 - Gabriel
1
@SolarWind 谢谢!能正常工作的 GIF 没有设置 dmPrevious 的处理模式。dmPrevious 主要用于每帧只更新图像的一小部分的 GIF。我在我的电脑上没有这样的 GIF。 - Tom Brunberg
@kobik 我也和你想的一样,尝试了一下,但没有效果。可能在渲染器创建后设置GIF.Animate会有帮助,但那也是一样的。 - Tom Brunberg
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接