2012-04-27 6 views
7

Ich verwende TGifImage, das in Delphi XE enthalten ist.TGifImage Transparenz Problem

Was ich versuche zu tun ist ein Gif aus einer Datei laden und extrahieren Sie alle Frames in eine Bitmap.

Das ist, was ich bisher getan:

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; 

Das Problem, das ich bin vor ist mit einem gewissen Transparenz Problem mit vielen verschiedenen Gifs, und auch Größe Problemen.

Hier sind einige Beispiele Bitmaps, die meinen Code gespeichert wurden unter Verwendung von oben:

enter image description here enter image description here

Wie Sie die Ergebnisse nicht so toll zu sehen sind, haben sie eine Größe und Fragen der Transparenz.

Ich weiß, die Gif-Dateien selbst sind nicht beschädigt, weil ich sie über meinen Webbrowser laden kann und sie korrekt ohne Fehler angezeigt werden.

Wie kann ich ein Gif aus File laden, jeden Frame Bitmap zuweisen, ohne Qualität zu verlieren?

+2

ich die Art und Weise herausgefunden, wie der Schlüsselrahmen realisiert werden. Es wird durch 'GraphicControlExtension.Disposal' für jeden Frame (falls verfügbar) bestimmt. Es bedeutet, was der aktuelle Frame mit dem bereits gerenderten Puffer tun soll. Als Quelle können Sie etwas anderes als 'TGIFRenderer' verwenden, die Klasse, die die Animationen tatsächlich rendert. Ich werde versuchen abends ein Beispiel zu komponieren (wenn jemand nicht schneller wäre :-) muss ich jetzt gehen ... – TLama

Antwort

10

Für ältere Delphi-Versionen (Pre 2009): Werfen Sie einen Blick auf den Code von GIFImage Einheit, könnten Sie prüfen möchten, wie TGIFPainter die Disposal Methode basiert Bilder macht auf jeden Frame.

Ich habe einen kleinen Code mit TGIFPainter.OnAfterPaint Event-Handler geschrieben, um den aktiven Rahmen zu BMP zu speichern, und alle "harte Arbeit" zu tun.

Hinweis: GIFImage Einheit Version 2.2 Release: 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; 

Hinweis: keine Fehlerbehandlung, den Code zu vereinfachen.

output bitmaps


Für neuere Delphi-Versionen (2009+): mit build-in GIFImg Einheit, können Sie dies mit dem Einsatz von TGIFRenderer einfach beenden (die ganz alten ersetzt TGIFPainter) z.B.:

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; 

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

Ausgezeichnet, das zweite Beispiel funktioniert perfekt für mich (Delphi XE). Ich wünschte, ich könnte mehrere Antworten in diesem Szenario akzeptieren, danke an alle. Wenn man diesen Code studiert, sieht es einfacher aus als ich erwartet hatte. –

+5

+1, ich denke, die Verwendung des Renderers ist der natürlichste Weg, dies zu tun. – TLama

2

Die Frames einer animierten GIF-Datei enthalten oft nur die Unterschiede zum vorherigen Frame (eine Optimierungstechnik zur Reduzierung der Dateigröße). Um also einen Schnappschuss des GIFs an einem bestimmten Punkt zu erzeugen, müssen Sie alle Frames bis zu diesem Punkt nacheinander einfügen.

Wir können dies erreichen, indem Sie Draw() mit seiner ‚ziehen transparent‘ Option Set:

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: Es gibt noch andere Elemente in die animierte GIF-Format, das die Menge an Zeiten Frames angeben, wiederholt werden usw., aber sie können dich nicht betreffen.

+0

habe ich vergessen zu erwähnen das ich das schon probiert habe, kein unterschied. Es scheint so, als ob einige Frames in einem GIF nur die zusätzlichen Bits zeichnen und vorherige Frames verwenden, um den Hintergrund zu zeichnen? –

+0

Ok, habe meine Antwort geändert :-) –

+0

Ich war eigentlich nah dann bezüglich meines letzten Kommentars! Nicht sicher, wie ich das erreichen könnte? –