2012-04-27 34 views
7

Tôi đang sử dụng TGifImage đi kèm với Delphi XE.Sự cố minh bạch TGifImage

Điều tôi đang cố gắng làm là tải một Gif từ một Tệp và trích xuất tất cả các khung thành một Ảnh bitmap.

Đây là những gì tôi đã làm cho đến nay:

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; 

Vấn đề tôi đang phải đối mặt là với một số vấn đề minh bạch với rất nhiều định dạng GIF khác nhau, và cũng có vấn đề kích thước.

Dưới đây là một số ví dụ mà bitmap đã được cứu sử dụng mã của tôi trên:

enter image description here enter image description here

Như bạn có thể thấy kết quả không tốt, họ có vấn đề kích thước và tính minh bạch.

Tôi biết bản thân tệp Gif không bị hỏng vì tôi có thể tải chúng qua trình duyệt web của tôi và chúng hiển thị chính xác mà không có lỗi.

Làm cách nào để tải Gif từ Tệp, gán từng khung cho Bitmap mà không làm mất chất lượng?

+2

tôi phát hiện ra cách làm thế nào các khung chính được thực hiện. Nó được xác định bởi 'GraphicControlExtension.Disposal' cho mỗi khung (nếu có). Nó có nghĩa là khung hiện tại sẽ làm gì với bộ đệm đã được render. Là một nguồn, bạn có thể sử dụng những gì khác hơn 'TGIFRenderer', lớp thực sự hiển thị các hình động. Tôi sẽ cố gắng để soạn một ví dụ vào buổi tối (nếu ai đó sẽ không nhanh hơn :-) Tôi phải đi ngay bây giờ ... – TLama

Trả lời

10

Đối với phiên bản Delphi cũ (Pre 2009): Hãy nhìn vào mã của GIFImage đơn vị, bạn có thể muốn kiểm tra như thế nào TGIFPainter ám chỉ rằng hình ảnh dựa trên phương pháp của mỗi Khung Disposal.

Tôi đã viết một mã nhỏ sử dụng TGIFPainter.OnAfterPaint trình xử lý sự kiện để lưu khung hoạt động vào BMP và thực hiện tất cả "công việc khó khăn".

Lưu ý: GIFImage phiên bản đơn vị 2.2 Phát hành: 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; 

Lưu ý: Không xử lý để đơn giản hóa các mã lỗi.

output bitmaps


Đối với phiên bản Delphi mới hơn (2009+): Với build-in GIFImg đơn vị, bạn có thể làm điều này từ bỏ dễ dàng với việc sử dụng TGIFRenderer (mà hoàn toàn thay thế cũ TGIFPainter) ví dụ:

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; 

Sử dụng 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

Tuyệt vời, ví dụ thứ hai hoạt động hoàn hảo cho tôi (Delphi XE). Tôi ước tôi có thể chấp nhận nhiều câu trả lời trong kịch bản này, cảm ơn tất cả mọi người. Nghiên cứu mã này làm cho nó trông dễ dàng hơn tôi đã dự đoán. –

+5

+1, tôi nghĩ rằng việc sử dụng trình kết xuất là cách tự nhiên nhất để thực hiện việc này. – TLama

2

Các khung của tệp GIF động chỉ thường chứa các khác biệt so với khung trước đó (một kỹ thuật tối ưu hóa để giảm kích thước tệp). Vì vậy, để tạo ra một bản chụp của GIF tại một điểm cụ thể, bạn sẽ phải dán tất cả các khung lên đến điểm đó, cái kia.

Chúng ta có thể đạt được điều này bằng cách sử dụng Draw() với 'vẽ minh bạch' tùy chọn thiết lập của nó:

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: Có những yếu tố khác sang định dạng GIF động, trong đó quy định số lượng khung thời gian được phải được lặp lại vv nhưng họ có thể không quan tâm bạn.

+0

Tôi quên đề cập đến tôi đã cố gắng đó, không có sự khác biệt. Dường như một số khung hình trong gif chỉ vẽ các bit phụ và sử dụng các khung trước đó để vẽ nền? –

+0

Ok, đã sửa đổi câu trả lời của tôi :-) –

+0

Tôi đã thực sự gần sau đó về bình luận cuối cùng của tôi! Bạn không chắc chắn làm thế nào tôi có thể đạt được điều này mặc dù? –

Các vấn đề liên quan