2012-04-27 2 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 파일 자체가 손상되지 않은 것을 알고 있습니다. 왜냐하면 웹 브라우저를 통해로드 할 수 있고 오류없이 올바르게 표시 할 수 있기 때문입니다.

어떻게 파일에서 Gif를로드하고 품질을 유지하면서 각 프레임을 비트 맵에 할당 할 수 있습니까?

+2

내가 키 프레임을 실현하는 방법을 방법을 발견했다. 각 프레임 (사용 가능한 경우)에 대한 'GraphicControlExtension.Disposal'에 의해 결정됩니다. 현재 프레임이 이미 렌더링 된 버퍼로 수행해야하는 작업을 의미합니다. 소스로서 실제로 애니메이션을 렌더링하는 클래스 인'TGIFRenderer' 이외의 것을 사용할 수 있습니다. 나는 저녁 시간에 예를 쓰기 위해 노력할 것이다. (만약 누군가가 더 빠르지 않으면 :-) 나는 이제 가야만한다. ... – TLama

답변

10

(전 2009 년) :GIFImage 기기의 코드를 살펴, 당신은 TGIFPainter 각 프레임의 Disposal 방법에 따라 이미지를 렌더링하는 방법을 확인 할 수 있습니다.

TGIFPainter.OnAfterPaint 이벤트 처리기를 사용하여 활성 프레임을 BMP에 저장하고 "힘든 작업"을 수행하는 작은 코드를 작성했습니다.

참고 : GIFImage 단위 버전 2.2 릴리스 : 5 (23 월 - 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; 

참고 : 코드를 단순화하기 위해 처리 오류 없음.

output bitmaps

새로운 델파이 버전 (2009+)에 대한

:와 구축에 GIFImg 단위, 당신은이 예를 들어, (완전 오래된 TGIFPainter 대체하는) TGIFRenderer의 사용으로 쉽게 종료 할 수:

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

우수, 두 번째 예제는 나를 위해 완벽하게 작동합니다 (Delphi XE). 이 시나리오에서 여러 답변을 받아 들일 수 있었으면 좋겠습니다. 모두에게 감사드립니다. 이 코드를 공부하면 내가 예상했던 것보다 쉽게 ​​보입니다. –

+5

+1, 렌더러를 사용하는 것이 가장 자연스러운 방법이라고 생각합니다. – TLama

2

애니메이션 GIF 파일의 프레임에는 이전 프레임과의 차이점 (파일 크기를 줄이는 최적화 기술) 만 포함되는 경우가 많습니다. 따라서 특정 지점에서 GIF의 스냅 샷을 생성하려면 모든 프레임을 차례대로 붙여야합니다.

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 형식에 다른 요소가있다 반복해야하는

우리는 '투명하게 그릴'옵션 세트 Draw()를 사용하여이를 달성 할 수 그러나 그들은 당신을 걱정하지 않을지도 모른다. 이전 델파이 버전

+0

나는 내가 그 것을 시도했음을 언급하는 것을 잊었다. 차이는 없다. GIF의 일부 프레임은 여분의 비트 만 그리는 것처럼 보이고 이전 프레임을 사용하여 배경을 그리는 것처럼 보입니다. –

+0

좋아, 내 대답을 수정했습니다 :-) –

+0

나는 실제로 내 마지막 코멘트에 관해서는 그때 가깝습니다! 어떻게 내가 이것을 이룰 수 있었는지 명확히하지 않고있다? –