[FMX] FMX.Graphics.Android 中对 GIF 判断的问题的修正

关于这个 Bug 的详情,大家可以参考:https://quality.embarcadero.com/browse/RSP-12424

Berlin 中的 FMX.Graphics.Android 实现的原始代码如下:

class function TBitmapCodecAndroid.IsGIFStream(const Stream: TStream): Boolean;
const
  IDCharCount = 3;
var
  PrevPosition: Int64;
  Builder: TStringBuilder;
  I: Integer;
  Value: Char;
begin
  if (Stream = nil) or (Stream.Size < IDCharCount) then
    Exit(False);

  PrevPosition := Stream.Position;
  try
    Builder := TStringBuilder.Create(IDCharCount);
    try
      for I := 0 to 2 do
      begin
        Stream.ReadBuffer(Value, 1);
        Builder.Append(Value);
      end;

      Result := SameText(Builder.ToString, 'GIF');
    finally
      Builder.Free;
    end;
  finally
    Stream.Position := PrevPosition;
  end;
end;

存在的问题:

  • 第一个判定不完善,流的大小大于 GIF 头部大小并不代表内容从当前位置读取剩余的内容长度大于3,所以这块应该改成
    if (Stream = nil) or (Stream.Size-Stream.Position < IDCharCount) then
        Exit(False);
  • 另一个问题是一个低级的问题,Char 类型在 Android 上对应的是 WideChar,也就是说是 2 个字节,然后它没有初始化,直接读取。由于 Value 的值为随机值,所以,有可能两个字节的内容不为空,结果就造成添加时出现问题,造成正确的GIF文件格式无法读取。实际上只需要 Value 初始为 0 就可以了。
  • 吐个槽:这么简单判定为啥要用 TStringBuilder?这让我想起一个笑话,说是为了让用户掏钱,故意在程序中加入 Sleep 来减慢程序的速度,这样以后借口优化就可以让用户掏更多的钱了。再怎么说,TStringBuilder也是一个对象,而且牵涉到内存的额外分配释放,纯属脱裤子放屁,多此一举。

好吧,再补充一点吧:GIF 实际上有两种头,一种头是 GIF89a,另外一种是 GIF87a,无论那种,头部实际上是6个字节,而不是 3 个,如果象上面只判断 GIF ,显然有失严谨。

给一个参考的修正代码:

class function TBitmapCodecAndroid.IsGIFStream(const Stream: TStream): Boolean;
const
  IDCharCount = 6;
  ID_GIF89a: array [0 .. 7] of Byte = ($47, $49, $46, $38, $39, $61,$00,$00);
  ID_GIF87a: array [0 .. 7] of Byte = ($47, $49, $46, $38, $37, $61,$00,$00);
var
  PrevPosition: Int64;
  I: Integer;
  Id: Int64;
begin
  if (Stream = nil) or (Stream.Size - Stream.Position < IDCharCount) then
    Exit(False);
  PrevPosition := Stream.Position;
  try
    Id:=0;
    Stream.ReadBuffer(Id, IDCharCount);
    Result :=(Id=PInt64(@Id_GIF89a[0])^) or (Id=PInt64(@Id_GIF87a[0])^);
  finally
    Stream.Position := PrevPosition;
  end;
end;

这里故意填充了ID到8位,以便直接将 GIF 头当成64位整数来比较,提高效率。

分享到: