11

我发现使用 Delphi 2009 创建的动画 GIFTGIFImage有时无法在某些GIF 查看器中正确播放。问题是动画过早地重新启动。

考虑以下示例:

program GIFAnomaly;

{$APPTYPE CONSOLE}

uses
  Windows, Types, Classes, SysUtils, Graphics, GIFImg;

var
  g: TGIFImage;
  bm: TBitmap;

procedure MakeFrame(n: integer);
var
  x: Integer;
  y: Integer;
begin
  for x := 0 to 256 - 1 do
    for y := 0 to 256 - 1 do
      bm.Canvas.Pixels[x, y] := RGB((x + n) mod 255,
        (x + y - 2*n) mod 255, (x*y*n div 500) mod 255);
end;

var
  i: integer;

begin

  bm := TBitmap.Create;
  bm.SetSize(256, 256);

  g := TGIFImage.Create;
  g.Animate := true;
  for i := 0 to 499 do
  begin
    MakeFrame(i);
    TGIFGraphicControlExtension.Create(g.Add(bm)).Delay := 3;
    Writeln('Creating frame ', i+1, ' of 500.');
  end;
  TGIFAppExtNSLoop.Create(g.Images.Frames[0]).Loops := 0;

  g.SaveToFile('C:\Users\Andreas Rejbrand\Desktop\test.gif');


end.

(这是我能找到的最简单的例子,它展示了这个问题。)

输出是一个相当大的动画 GIF。在 Internet Explorer 11 中,整个 15 秒的“电影”可以正常播放,但在 Google Chrome 中,“电影”在大约 4 秒后过早地重新启动。

为什么是这样?

  1. 输出的 GIF 文件有问题吗?
  2. 如果是这样,我上面的代码有问题,还是有问题GIFImg
  3. 如果不是,查看器中问题的性质是什么?有多少可用的观众有这个问题?有没有办法在 GIF 创建过程中“避免”这个问题?

为了 SO 用户的利益,上面的代码是一个最小的工作示例。当然,当我发现这个问题时,我并没有创造这些迷幻模式。相反,我正在研究 Lorenz 系统模拟器,并制作了这个在 IE 中播放但在 Chrome 中不播放的 GIF 动画:

显示问题的示例 GIF 动画

在 Internet Explorer 11 中,模型会在动画重新启动之前旋转 360 度。在谷歌浏览器中,动画在大约 20 度后过早重新启动。

  • Lorenz 图像适用于 Internet Explorer 11.0.9600.17239、GIMP 2.8.0、Opera 12.16
  • Lorenz 图像在 Google Chrome 36.0.1985.143 m、Firefox 26.0、27.0.1、31.0 中不起作用

如果我在 The GIMP 中打开一个“有问题的”GIF 并让 GIMP(重新)将其保存为动画 GIF,则结果适用于每个查看器。以下是 Lorenz 动画的 GIMPed 版本:

已被 GIMPed 的示例 GIF 动画

使用十六进制编辑器比较这两个文件,并使用Wikipedia 文章作为参考,例如,似乎 'NETSCAPE' 字符串在原始(unGIMPed)版本中的位置错误。有点奇怪的是,即使我设置了GIF 图像的widthheight,逻辑屏幕描述符中的对应值也不存在。

4

2 回答 2

6

这是 TGIFImage 的 LZW 编码器中的一个错误。

在一些非常罕见的情况下,LZW 编码器会在 LZW 流结束时输出一个额外的零字节。由于 LZW 结束块标记也是一个零字节,严格的 GIF 阅读器可能会对此感到窒息或将其解释为 GIF 的结束(尽管文件结束标记是 $3B)。

一些 GIF 阅读器可以处理此问题的原因可能是存在此问题的 GIF 在多年前很常见。显然 TGIFImage 并不是唯一犯这个错误的库。

gifimg.pas要解决此问题,请对(标有*的更改)进行以下修改:

procedure TGIFWriter.FlushBuffer;
begin
  if (FNeedsFlush) then
  begin
    FBuffer[0] := Byte(FBufferCount-1); // Block size excluding the count
    Stream.WriteBuffer(FBuffer, FBufferCount);
    FBufferCount := 1; // Reserve first byte of buffer for length
    FNeedsFlush := False; // *** Add this ***
  end;
end;
于 2014-08-25T14:47:23.703 回答
1

编辑:事实证明这不是答案,但我保留它,因为关于循环扩展的规则仍然适用。


NETSCAPE 循环扩展必须是第一个扩展:

var
  Frame: TGIFFrame;
...
for i := 0 to 499 do
begin
  MakeFrame(i);
  Frame := g.Add(bm);
  if (i = 0) then
    TGIFAppExtNSLoop.Create(Frame).Loops := 0;
  TGIFGraphicControlExtension.Create(Frame).Delay := 3;
  Writeln('Creating frame ', i+1, ' of 500.');
end;

请参阅:TGIFImage 常见问题解答

除此之外,我认为您的 GIF 没有任何问题,但您可以使用全局颜色表稍微减小大小。

于 2014-08-25T06:59:30.810 回答