2013-02-28 5 views
5

Chcę wyodrębnić ramki obrazu GIF. Poniższy kod działa, ale to nie jest to, czego potrzebuję. Muszę zachować wyodrębnione ramki w serii bitmap.Nie można narysować GIF na dynamicznie utworzonym TBitmap (ach)

procedure TForm1.Button2Click(Sender: TObject); 
var 
    GIF: TGIFImage; 
    Bitmap: TBitmap; 
    I: Integer; 
    GR: TGIFRenderer; 
    R: TRect; 
begin 
    GIF := TGIFImage.Create; 
    TRY 
    GIF.LoadFromFile('c:\1.gif'); 
    Bitmap := TBitmap.Create;  <------------ one single object, reused 
    Bitmap.SetSize(GIF.Width, GIF.Height); 
    GR := TGIFRenderer.Create(GIF); 
    try 
     for I := 0 to GIF.Images.Count - 1 do 
     begin  
     GR.Draw(Bitmap.Canvas, Bitmap.Canvas.ClipRect); 

     Self.Canvas.Draw(0, 0, Bitmap); 

     GR.NextFrame; 
     end; 
    finally 
     GR.Free; 
    end; 
    finally 
    GIF.Free; 
    //Bitmap.Free; 
    end; 
end; 

Dlatego dynamicznie tworzę mapę bitową dla każdej ramki. Ale to nie zadziała. Wyświetli tylko tę samą/pierwszą klatkę we wszystkich bitmapach!

procedure TForm1.Button2Click(Sender: TObject); 
var 
    GIF: TGIFImage; 
    Bitmap: TBitmap; 
    I: Integer; 
    GR: TGIFRenderer; 
    R: TRect; 
begin 
    GIF := TGIFImage.Create; 
    TRY 
    GIF.LoadFromFile('c:\1.gif'); 
    GR := TGIFRenderer.Create(GIF); 
    try 
     for I := 0 to GIF.Images.Count - 1 do 
     begin 
     Bitmap := TBitmap.Create;  <----- multiple bitmaps, one for each frame 
     Bitmap.SetSize(GIF.Width, GIF.Height); 

     GR.Draw(Bitmap.Canvas, Bitmap.Canvas.ClipRect); 

     Self.Canvas.Draw(0, 0, Bitmap); 

     GR.NextFrame; 
     end; 
    finally 
     GR.Free; 
    end; 

    {TODO: store bitmaps in a TObjectList for later use 
    List.Add(Bitmap); }  
    finally 
    GIF.Free; 
    end; 
end; 

Co jest nie tak z powyższym kodem? Może TGIFRenderer rysuje TYLKO różnice między ramkami?


UPDATE dla TLama/jachguate:

TLama mówi, że kod nie działa, bo nie uwolnić bitmap. Nie chcę zwolnić bitmap. Potrzebuję ich później. Oto kilka (klasy demo) kodu.

VAR List: TObjectList; {used and freed somwhere else} 

procedure TForm1.Button2Click(Sender: TObject); 
var 
    GIF: TGIFImage; 
    UniqueBMP: TBitmap; 
    I: Integer; 
    GR: TGIFRenderer; 
    R: TRect; 
begin 
    List:= TObjectList.Create; 
    GIF := TGIFImage.Create; 
    TRY 
    GIF.LoadFromFile('c:\1.gif'); 

    GR := TGIFRenderer.Create(GIF); 
    try 
     for I := 0 to GIF.Images.Count - 1 do 
     begin 
     UniqueBMP := TBitmap.Create; 
     UniqueBMP.SetSize(GIF.Width, GIF.Height); 

     if GIF.Images[I].Empty then Break; 

     GR.Draw(UniqueBMP.Canvas, UniqueBMP.Canvas.ClipRect); 

     Self.Canvas.Draw(0, 0, UniqueBMP); 
     Sleep(50); 

     List.Add(UniqueBMP); 

     GR.NextFrame; 
     end; 
    finally 
     GR.Free; 
    end; 
    finally 
    GIF.Free; 
    end; 
end; 


procedure TForm1.btnFreeClick(Sender: TObject); 
begin 
    FreeAndNil(List); 
end; 
+0

WIEM! Zignoruj ​​to! To tylko wersja demo. Nie ma to związku z naszym aktualnym problemem :) – Ampere

+1

Nic nie wyjaśnia, ale podejrzewam, że przeplatałam klatkę. Domyślam się, że renderer rysuje ramkę dokładnie tak, jak jest (dlaczego by tak nie było, a ponieważ zawsze używasz nowej mapy bitowej dla każdej ramki, możesz uzyskać (wizualnie) prawie pustą bitmapę, ponieważ (wzrokowo) reszta musi być renderowana przez leżące poniżej ramy. Innymi słowy, musisz użyć jednej bitmapy i pozwolić rendererowi narysować dla ciebie warstwy. Ale to tylko moje przypuszczenie. – TLama

+0

@TLama - Myślałem o tym samym, ale obraz GIF, którego używam, jest dość animowany. Istnieje wiele różnic między ramkami. Powinienem móc zobaczyć coś w następnych klatkach ... – Ampere

Odpowiedz

9

W TCustomGIFRenderer.Draw sprawdza płótno, na którym ma zamiar uczynić. Jeśli różni się od tego, który zapamiętuje z ostatniego renderowania (i różni się, ponieważ tworzysz nową mapę bitową dla każdej klatki), wywoływana jest metoda TCustomGIFRenderer.Reset, która, jak wyjaśnia jej nazwa, resetuje indeks ramki do 0 Właśnie dlatego renderujesz zawsze tylko pierwszą klatkę.

+3

Dzięki TLama za wyjaśnienie. Nie, dziękuję Embarcadero za ich "wspaniałą" instrukcję obsługi. – Ampere

+0

Nie ma za co! Tak, powinno to być wspomniane w dokumentach lub lepiej wdrożone w inny sposób. – TLama

3

kod Pracując w oparciu o rozwiązania TLama za (proszę głosować swoje stanowisko nie moje):

procedure TForm1.Button2Click(Sender: TObject); 
var 
    GIF: TGIFImage; 
    TempBMP, UniqueBMP: TBitmap; 
    I: Integer; 
    GR: TGIFRenderer; 
    R: TRect; 
begin 
    GIF := TGIFImage.Create; 
    TRY 
    GIF.LoadFromFile('c:\1.gif'); 

    TempBMP := TBitmap.Create;  <------- SOLUTION 
    TempBMP.SetSize(GIF.Width, GIF.Height); 

    GR := TGIFRenderer.Create(GIF); 
    try 
     for I := 0 to GIF.Images.Count - 1 do 
     begin 
     UniqueBMP := TBitmap.Create;  <------- SOLUTION 
     UniqueBMP.SetSize(GIF.Width, GIF.Height); 

     if GIF.Images[I].Empty then Break; 

     GR.Draw(TempBMP.Canvas, TempBMP.Canvas.ClipRect); 

     UniqueBMP.Assign(TempBMP);    <------- SOLUTION 
     Self.Canvas.Draw(0, 0, UniqueBMP); 
     Sleep(50); 

     GR.NextFrame; 
     end; 
    finally 
     GR.Free; 
    end; 
    finally 
    GIF.Free; 
    TempBMP.Free; 
    end; 
end; 
+0

To rozwiązanie jest w porządku. Aby pozwolić rendererowi narysować ramkę bieżącego indeksu, którego musisz użyć dla metody 'GR.Draw', wywołaj to samo płótno i ten sam prostokąt. Gdy którykolwiek z tych parametrów różni się od pierwszego wywołania metody "GR.Draw", indeks ramki zostanie zresetowany do pierwszej klatki. – TLama

Powiązane problemy