Delphi TGIFImage animation issue with some GIF vie

2019-04-21 05:49发布

问题:

I have discovered that animated GIFs created using Delphi 2009's TGIFImage sometimes doesn't play correctly in some GIF viewers. The problem is that the animation is restarted prematurely.

Consider the following example:

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.

(This is the simplest example I could find that exhibits the problem.)

The output is a rather large animated GIF. In Internet Explorer 11, the entire 15-second 'movie' is played properly, but in Google Chrome the 'movie' is prematurely restarted after only about four seconds.

Why is this?

  1. Is there something wrong with the output GIF file?
  2. If so, is there something wrong with my code above, or is there a problem with GIFImg?
  3. If not, what is the nature of the problem in the viewer? What fraction of the available viewers have this problem? Is there a way to 'avoid' this problem during GIF creation?

For the benefit of the SO user, the above code is a minimal working example. Of course, I wasn't creating these psychedelic patterns when I discovered the issue. Instead, I was working on a Lorenz system simulator, and produced this GIF animation which does play in IE but not in Chrome:

Sample GIF animation displaying the issue http://privat.rejbrand.se/lorenz50.gif

In Internet Explorer 11, the model is rotated 360 degrees before the animation is restarted. In Google Chrome, the animation is restarted prematurely after only some 20 degrees.

  • The Lorenz image works in Internet Explorer 11.0.9600.17239, The GIMP 2.8.0, Opera 12.16
  • The Lorenz image does not work in Google Chrome 36.0.1985.143 m, Firefox 26.0, 27.0.1, 31.0.

If I open a 'problematic' GIF in The GIMP and let GIMP (re)save it as an animated GIF, the result works in every viewer. The following is the GIMPed version of the Lorenz animation:

Sample GIF animation that has been GIMPed http://privat.rejbrand.se/lorenz50_GIMPed.gif

Comparing the two files using a hex editor, and using the Wikipedia article as a reference, it seems, for instance, like the 'NETSCAPE' string is at the wrong place in the original (unGIMPed) version. It is somewhat strange, that even if I set the width and height of the GIF image, the corresponding values in the Logical Screen Descriptor are not there.

回答1:

It's a bug in TGIFImage's LZW encoder.

In some very rare circumstances the LZW encoder will output an extra zero byte at the end of the LZW steam. Since the LZW end block marker is also a zero byte, a strict GIF reader might choke on this or interpret it as the end of the GIF (although the end of file marker is $3B).

The reason some GIF readers can handle this is probably that GIFs with this problem was common many years ago. Apparently TGIFImage wasn't the only library to make that particular mistake.

To fix the problem make the following modification to gifimg.pas (change marked with *):

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;


回答2:

Edit: This turned out not to be the answer but I'm keeping it as the rule about the loop extension still applies.


The NETSCAPE loop extension must be the first extension:

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;

See: The TGIFImage FAQ.

Apart from that I see nothing wrong with your GIF, but you could reduce the size a bit with a global color table.