首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

GDI+ 進階 (103):TGPImage 提取 GIF 動畫每一幀

     編輯:Delphi

目標

使用 GDI+ TGPImage 載入 GIF 動畫,枚舉所有幀,並在 TListBox 中選擇顯示指定幀。

核心 API:幀維度與選擇

方法用途
GetFrameDimensionsCount獲取幀維度數量 (GIF 通常為 1:時間維度)
GetFrameDimensionsList填充 GUID 陣列,標識每個維度 (如 FrameDimensionTime)
GetFrameCount(DimensionID)獲取指定維度的總幀數
SelectActiveFrame(DimensionID, FrameIndex)選擇當前顯示的幀 (0 基索引)

完整實戰代碼


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    btnOpen: TButton;
    lbFrames: TListBox;
    OpenDialog1: TOpenDialog;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnOpenClick(Sender: TObject);
    procedure FormPaint(Sender: TObject);
    procedure lbFramesClick(Sender: TObject);
  private
    FGifFrame, FGifFrameCount: Word;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses GDIPOBJ, GDIPAPI;

var
  GImg: TGPImage;

procedure TForm1.FormCreate(Sender: TObject);
begin
  OpenDialog1.Filter := 'GIF 動畫|*.gif';
  GImg := TGPImage.Create;
  lbFrames.Align := alLeft;
  lbFrames.Width := 100;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  GImg.Free;
end;

procedure TForm1.btnOpenClick(Sender: TObject);
var
  DimCount: Integer;
  DimIDs: PGUID;
  i: Integer;
  DynIDs: array of TGUID;
begin
  if not OpenDialog1.Execute then Exit;

  GImg.Free;
  GImg := TGPImage.Create(OpenDialog1.FileName);

  // 1. 獲取維度 GUID 列表
  DimCount := GImg.GetFrameDimensionsCount;
  GetMem(DimIDs, DimCount * SizeOf(TGUID));
  try
    GImg.GetFrameDimensionsList(DimIDs, DimCount);
    // 複製到動態陣列方便索引
    SetLength(DynIDs, DimCount);
    Move(DimIDs^, DynIDs[0], DimCount * SizeOf(TGUID));

    // 2. 獲取時間維度的總幀數 (通常第 0 個維度)
    FGifFrameCount := GImg.GetFrameCount(DynIDs[0]);
    Caption := Format('GIF 幀數: %d', [FGifFrameCount]);

    // 3. 填充列表
    lbFrames.Clear;
    for i := 1 to FGifFrameCount do
      lbFrames.Items.Add(Format('第 %d 幀', [i]));
    lbFrames.ItemIndex := 0;
    FGifFrame := 0;
    GImg.SelectActiveFrame(FrameDimensionTime, 0);
  finally
    FreeMem(DimIDs);
  end;
  Invalidate;
end;

procedure TForm1.FormPaint(Sender: TObject);
var
  Gr: TGPGraphics;
begin
  if Assigned(GImg) then
  begin
    Gr := TGPGraphics.Create(Canvas.Handle);
    try
      // 繪製當前選中幀
      Gr.DrawImage(GImg, lbFrames.Width + 10, 10, GImg.GetWidth, GImg.GetHeight);
    finally
      Gr.Free;
    end;
  end;
end;

procedure TForm1.lbFramesClick(Sender: TObject);
begin
  FGifFrame := lbFrames.ItemIndex;
  if Assigned(GImg) then
  begin
    GImg.SelectActiveFrame(FrameDimensionTime, FGifFrame);
    Invalidate;
  end;
end;

end.

DFM 佈局要點


object Form1: TForm1
  ClientHeight = 300
  ClientWidth = 500
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  OnPaint = FormPaint
  object lbFrames: TListBox
    Align = alLeft
    Width = 100
    OnClick = lbFramesClick
  end
  object btnOpen: TButton
    Left = 400
    Top = 260
    Caption = '打開 GIF'
    OnClick = btnOpenClick
  end
  object OpenDialog1: TOpenDialog
    Filter = 'GIF 動畫|*.gif'
  end
end

關鍵技術細節

1. FrameDimensionTime 是預定義 GUID

定義在 GDIPAPI 中:


const
  FrameDimensionTime: TGUID = '{6AEDBD6D-3A51-4E5D-A5E2-2B5C5A5E5F5F}'; // 示例值,實際以單元為準

GIF 動畫唯一維度即時間,故直接用此常量調用 SelectActiveFrame。

2. 幀索引 0 基

SelectActiveFrame(DimID, 0) 為第一幀,FrameCount-1 為最後一幀。

3. 繪製時自動顯示當前活動幀

TGPGraphics.DrawImage(GImg, ...) 會繪製 GImg 當前 SelectActiveFrame 設置的幀,無需額外參數指定幀索引。

4. 內存管理

  • GetMem/FreeMem 配對釋放 PGUID 緩衝區。
  • TGPImage.Create(FileName) 內部解碼全幀,大 GIF 可能佔用較多內存。

擴展:導出幀為獨立圖片


procedure ExportFrames(const GifPath, OutDir: string);
var
  Img: TGPImage;
  Count, i: Integer;
  DimIDs: array of TGUID;
  DimCount: Integer;
  P: PGUID;
  Gr: TGPGraphics;
  Bmp: TGPBitmap;
  EncCLSID: TGUID;
begin
  Img := TGPImage.Create(GifPath);
  try
    DimCount := Img.GetFrameDimensionsCount;
    SetLength(DimIDs, DimCount);
    GetMem(P, DimCount * SizeOf(TGUID));
    try
      Img.GetFrameDimensionsList(P, DimCount);
      Move(P^, DimIDs[0], DimCount * SizeOf(TGUID));
      Count := Img.GetFrameCount(DimIDs[0]);
      GetEncoderClsid('image/png', EncCLSID); // 需 GDIPUTIL
      for i := 0 to Count - 1 do
      begin
        Img.SelectActiveFrame(FrameDimensionTime, i);
        Bmp := TGPBitmap.Create(Img.GetWidth, Img.GetHeight);
        try
          Gr := TGPGraphics.Create(Bmp);
          try
            Gr.DrawImage(Img, 0, 0, Img.GetWidth, Img.GetHeight);
          finally
            Gr.Free;
          end;
          Bmp.Save(IncludeTrailingPathDelimiter(OutDir) + Format('frame_%03d.png', [i]), EncCLSID);
        finally
          Bmp.Free;
        end;
      end;
    finally
      FreeMem(P);
    end;
  finally
    Img.Free;
  end;
end;

總結

GDI+ 原生支援 GIF 多幀提取:GetFrameDimensionsList → GetFrameCount → SelectActiveFrame → DrawImage。核心在於理解「維度 GUID」概念,GIF 僅有時間維度,配合 FrameDimensionTime 常量即可隨意跳轉、顯示、導出任意幀。此技術適用於 GIF 預覽器、幀編輯器、動畫分析工具開發。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved