2012-04-27 7 views
7

私はDelphi XEに含まれているTGifImageを使用しています。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ファイルの多くの問題、およびサイズの問題です。ここで

は、上記の私のコードを使用して保存されたいくつかの例のビットマップです:

enter image description here enter image description here

あなたは結果が素晴らしいではありません見ることができるように、彼らは大きさと透明性の問題があります。

私はGifファイル自体が壊れていないことを知っています。なぜなら、私はそれらをWebブラウザから読み込み、正しく表示することができるからです。

ファイルからGIFをロードするには、品質を落とさずに各フレームをビットマップに割り当てますか?

+2

を私は、キーフレームが実現されているかの方法を見つけました。各フレーム(使用可能な場合)の 'GraphicControlExtension.Disposal'によって決定されます。現在のフレームが既にレンダリングされたバッファで何をすべきかを意味します。ソースとして、実際にアニメーションをレンダリングするクラスである 'TGIFRenderer'以外のものを使用することができます。私は夕方の模範を作ろうとします(もし誰かがもっと速くならないなら、今私は行かなければなりません...) – TLama

答えて

10

(事前2009):GIFImageユニットのコードを見てみましょう、あなたはTGIFPainterは、各フレームのDisposal法に基づいて画像をレンダリングする方法を確認したいかもしれません。

TGIFPainter.OnAfterPaintイベントハンドラを使用して小さなコードを書いて、アクティブフレームをBMPに保存し、すべての「苦労」をします。

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

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+)については

output bitmaps


:ビルドでGIFImg単位で、あなたはこれは、例えば(完全に古いTGIFPainterを置き換えた)TGIFRendererを利用して簡単に終了しないことができます:GDI +使用

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; 

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; 
+1

優秀、2番目の例は私のために完全に機能します(Delphi XE)。私はこのシナリオで複数の回答を受け入れることができれば幸いです。このコードを勉強することで、私が予想していたよりも見やすくなりました。 –

+5

+1、私はレンダラーを使うことがこれを行う最も自然な方法だと思います。 – TLama

2

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

私たちは、その「透過的に描く」オプションを設定して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; 

NB:アニメーションGIF形式に他の要素がありますが、時間フレームの量を指定繰り返されることになっていますあなたには関係しないかもしれません。古いDelphiのバージョンについては

+0

私はすでにそれを試してみましたが、違いはありません。あたかもGIFのいくつかのフレームが余分なビットを描画し、前のフレームを使って背景を描くように見えますか? –

+0

OK、私の答えを変更しました:-) –

+0

私は実際に私の最後のコメントに関して近いです!私はこれをどのように達成することができたか分かりませんか? –