使用 GDI+ TGPImage 載入 GIF 動畫,枚舉所有幀,並在 TListBox 中選擇顯示指定幀。
| 方法 | 用途 |
|---|---|
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.
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
FrameDimensionTime 是預定義 GUID定義在 GDIPAPI 中:
const
FrameDimensionTime: TGUID = '{6AEDBD6D-3A51-4E5D-A5E2-2B5C5A5E5F5F}'; // 示例值,實際以單元為準
GIF 動畫唯一維度即時間,故直接用此常量調用 SelectActiveFrame。
SelectActiveFrame(DimID, 0) 為第一幀,FrameCount-1 為最後一幀。
TGPGraphics.DrawImage(GImg, ...) 會繪製 GImg 當前 SelectActiveFrame 設置的幀,無需額外參數指定幀索引。
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 預覽器、幀編輯器、動畫分析工具開發。