7

Delphi XE に含まれている TGifImage を使用しています。

私がやろうとしているのは、ファイルから Gif をロードし、すべてのフレームをビットマップに抽出することです。

これは私がこれまで行ったことです:

procedure ExtractGifFrames(FileName: string);
var
  Gif: TGifImage;
  Bmp: TBitmap;
  i: Integer;
begin
  Gif := TGifImage.Create;
  try
    Gif.LoadFromFile(FileName);

    Bmp := TBitmap.Create;
    try
      Bmp.SetSize(Gif.Width, Gif.Height);

      for i := 0 to Gif.Images.Count - 1 do
      begin
        if not Gif.Images[i].Empty then
        begin
          Bmp.Assign(Gif.Images[i]);
          Bmp.SaveToFile('C:\test\bitmap' + IntToStr(i) + '.bmp');
        end;
      end;
    finally
      Bmp.Free;
    end;
  finally
    Gif.Free;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if OpenPictureDialog1.Execute then
  begin
    ExtractGifFrames(OpenPictureDialog1.FileName);
  end;
end;

私が直面している問題は、さまざまなGifの透明性の問題と、サイズの問題です。

上記のコードを使用して保存されたビットマップの例を次に示します。

ここに画像の説明を入力 ここに画像の説明を入力

結果が良くないことがわかるように、サイズと透明度の問題があります。

Gif ファイル自体が破損していないことはわかっています。Web ブラウザから読み込め、問題なく正しく表示されるからです。

ファイルからGifをロードし、品質を損なうことなく各フレームをビットマップに割り当てるにはどうすればよいですか?

4

2 に答える 2

10

Delphi の古いバージョン (2009 年より前) の場合:ユニットのコードを見て、各フレームのメソッドに基づいて画像がGIFImageどのようにレンダリングされるかを確認することをお勧めします。TGIFPainterDisposal

TGIFPainter.OnAfterPaintイベント ハンドラーを使用してアクティブなフレームを BMP に保存し、すべての「大変な作業」を行う小さなコードを作成しました。

注:GIFImageユニット バージョン 2.2 リリース: 5 (1999 年 5 月 23 日)

type
  TForm1 = class(TForm)
    Button1: TButton;
    ProgressBar1: TProgressBar;
    procedure Button1Click(Sender: TObject);
  public
    FBitmap: TBitmap;
    procedure AfterPaintGIF(Sender: TObject);
  end;

...

procedure TForm1.Button1Click(Sender: TObject);
var
  GIF: TGIFImage;
begin
  GIF := TGIFImage.Create;
  FBitmap := TBitmap.Create;
  Button1.Enabled := False;
  try
    GIF.LoadFromFile('c:\test\test.gif');
    GIF.DrawOptions := GIF.DrawOptions - [goLoop, goLoopContinously, goAsync];
    GIF.AnimationSpeed := 1000; // Max - no delay
    FBitmap.Width := GIF.Width;
    FBitmap.Height := GIF.Height;
    GIF.OnAfterPaint := AfterPaintGIF;

    ProgressBar1.Max := Gif.Images.Count;
    ProgressBar1.Position := 0;
    ProgressBar1.Smooth := True;
    ProgressBar1.Step := 1;

    // Paint the GIF onto FBitmap, Let TGIFPainter do the painting logic
    // AfterPaintGIF will fire for each Frame
    GIF.Paint(FBitmap.Canvas, FBitmap.Canvas.ClipRect, GIF.DrawOptions);
    ShowMessage('Done!');
  finally
    FBitmap.Free;
    GIF.Free;
    Button1.Enabled := True;
  end;
end;

procedure TForm1.AfterPaintGIF(Sender: TObject);
begin
  if not (Sender is TGIFPainter) then Exit;
  if not Assigned(FBitmap) then Exit;
  // The event will ignore Empty frames      
  FBitmap.Canvas.Lock;
  try
    FBitmap.SaveToFile(Format('%.2d.bmp', [TGIFPainter(Sender).ActiveImage]));
  finally
    FBitmap.Canvas.Unlock;
  end;
  ProgressBar1.StepIt;
end;

注: コードを簡素化するためのエラー処理はありません。

出力ビットマップ


新しいDelphiバージョン(2009年以降) の場合:組み込みユニットを使用すると、 (古いを完全に置き換えた)をGIFImg使用してこれを簡単に行うことができます。例:TGIFRendererTGIFPainter

procedure TForm1.Button1Click(Sender: TObject);
var
  GIF: TGIFImage;
  Bitmap: TBitmap;
  I: Integer;
  GR: TGIFRenderer;
begin
  GIF := TGIFImage.Create;      
  Bitmap := TBitmap.Create;
  try
    GIF.LoadFromFile('c:\test\test.gif');
    Bitmap.SetSize(GIF.Width, GIF.Height);
    GR := TGIFRenderer.Create(GIF);
    try
      for I := 0 to GIF.Images.Count - 1 do
      begin
        if GIF.Images[I].Empty then Break;
        GR.Draw(Bitmap.Canvas, Bitmap.Canvas.ClipRect);
        GR.NextFrame;
        Bitmap.SaveToFile(Format('%.2d.bmp', [I]));
      end;
    finally
      GR.Free;
    end;  
  finally
    GIF.Free;
    Bitmap.Free;
  end;
end;

GDI+ の使用:

uses ..., GDIPAPI, GDIPOBJ, GDIPUTIL;

procedure ExtractGifFrames(const FileName: string);
var
  GPImage: TGPImage;
  encoderClsid: TGUID;
  BmpFrame: TBitmap;
  MemStream: TMemoryStream;
  FrameCount, FrameIndex: Integer;
begin
  GPImage := TGPImage.Create(FileName);
  try
    if GPImage.GetLastStatus = Ok then
    begin
      GetEncoderClsid('image/bmp', encoderClsid);
      FrameCount := GPImage.GetFrameCount(GDIPAPI.FrameDimensionTime);
      for FrameIndex := 0 to FrameCount - 1 do
      begin
        GPImage.SelectActiveFrame(GDIPAPI.FrameDimensionTime, FrameIndex);
        MemStream := TMemoryStream.Create;
        try
          if GPImage.Save(TStreamAdapter.Create(MemStream), encoderClsid) = Ok then
          begin
            MemStream.Position := 0;
            BmpFrame := TBitmap.Create;
            try
              BmpFrame.LoadFromStream(MemStream);
              BmpFrame.SaveToFile(Format('%.2d.bmp', [FrameIndex]));
            finally
              BmpFrame.Free;
            end;
          end;
        finally
          MemStream.Free;
        end;
      end;
    end;
  finally
    GPImage.Free;
  end;
end;
于 2012-04-29T14:12:28.133 に答える
2

アニメーション GIF ファイルのフレームには、多くの場合、前のフレームとの違いのみが含まれます (ファイル サイズを縮小するための最適化手法)。そのため、特定の時点で GIF のスナップショットを作成するには、その時点までのすべてのフレームを 1 つずつ貼り付ける必要があります。

Draw()これは、「透過的に描画」オプション セットを使用することで実現できます。

procedure ExtractGifFrames(FileName: string);
var
  Gif: TGifImage;
  Bmp: TBitmap;
  i: Integer;
  Bounds: TRect;
begin
  Gif := TGifImage.Create;
  try
    Gif.LoadFromFile(FileName);
    Bounds := Rect(0, 0, Gif.Width-1, Gif.Height-1);

    Bmp := TBitmap.Create;
    try
      Bmp.SetSize(Gif.Width, Gif.Height);
      Bmp.PixelFormat := pf32bit;

      for i := 0 to Gif.Images.Count - 1 do
      begin
        if not Gif.Images[i].Empty then
        begin
          Gif.Images[i].Draw(Bmp.Canvas, Bounds, True, True);
          Bmp.SaveToFile(IntToStr(i) + '.bmp');
        end;
      end;
    finally
      Bmp.Free;
    end;
  finally
    Gif.Free;
  end;
end;

注意: アニメーション GIF 形式には、フレームが繰り返される回数などを指定する他の要素がありますが、それらは気にならないかもしれません。

于 2012-04-27T10:41:03.110 に答える