Проблема с анимацией Delphi TGIFImage с некоторыми зрителями GIF

Я обнаружил, что анимированные GIF файлы, созданные с помощью Delphi 2009 TGIFImage, иногда не воспроизводятся корректно в некоторых средствах просмотра 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 "фильм" преждевременно перезапускается примерно через четыре секунды.

Почему это?

  1. Что-то не так с выходным GIF файлом?
  2. Если да, то что-то не так с моим кодом выше или проблема с GIFImg?
  3. Если нет, какова природа проблемы у зрителя? Какая часть доступных зрителей имеет эту проблему? Есть ли способ избежать этой проблемы при создании GIF файла?

Для удобства пользователя SO приведенный выше код является минимальным рабочим примером. Конечно, я не создавал эти психоделические паттерны, когда обнаружил проблему. Вместо этого я работал над симулятором системы Lorenz и создал анимацию GIF, которая воспроизводится в IE, но не в Chrome:

Sample GIF animation displaying the issue

В Internet Explorer 11 модель поворачивается на 360 градусов, после чего анимация перезапускается. В Google Chrome анимация перезапускается преждевременно только после примерно 20 градусов.

  • Образ Лоренца работает в Internet Explorer 11.0.9600.17239, GIMP 2.8.0, Opera 12.16
  • Изображение Лоренца не работает в Google Chrome 36.0.1985.143 м, Firefox 26.0, 27.0.1, 31.0.

Если я открою "проблемный" GIF файл в GIMP и позволю GIMP (пере) сохранить его как анимированный GIF файл, результат будет работать для каждого зрителя. Ниже приводится GIMP-версия анимации Лоренца:

Sample GIF animation that has been GIMPed

Сравнение двух файлов с использованием шестнадцатеричного редактора и использование статьи Википедии в качестве ссылки, например, похоже, что строка "NETSCAPE" находится в неправильном месте в исходной (unGIMPed) версии. Несколько странно, что даже если я установлю width и height изображения GIF, соответствующие значения в дескрипторе логического экрана отсутствуют.

Ответ 1

Это ошибка в кодере 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;

Ответ 2

Изменить: Это оказалось не ответом, но я сохраняю его, так как правило о расширении цикла все еще применяется.


Расширение цикла 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, но вы можете немного уменьшить размер с помощью глобальной таблицы цветов.