GDI+ 原生支援靜態 GIF 直接繪製,但動態 GIF 需自行解析幀序列、延遲時間並實作播放迴圈。本文封裝 ImageEx : public Image 類別,自動偵測動畫屬性、啟動背景執行緒逐幀繪製,提供 Play()/Pause()/Stop() 控制介面。
GDI+ 內部已解析上述結構,透過 Image 類別方法暴露動畫屬性。
bool ImageEx::TestForAnimatedGIF()
{
UINT count = GetFrameDimensionsCount();
GUID* pDimIDs = new GUID[count];
GetFrameDimensionsList(pDimIDs, count);
m_nFrameCount = GetFrameCount(&pDimIDs[0]); // 總幀數
// 取得 PropertyTagFrameDelay (0x5100) 屬性項
int nSize = GetPropertyItemSize(PropertyTagFrameDelay);
m_pPropertyItem = (PropertyItem*)malloc(nSize);
GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
// m_pPropertyItem->value 為 long 陣列,每項 = 該幀延遲 (單位 1/100 秒)
delete[] pDimIDs;
return m_nFrameCount > 1;
}
bool ImageEx::DrawFrameGIF()
{
WaitForSingleObject(m_hPause, INFINITE); // 支援暫停/繼續
GUID pageGuid = FrameDimensionTime;
HDC hDC = GetDC(m_hWnd);
if (hDC) {
Graphics g(hDC);
g.DrawImage(this, m_rc.left, m_rc.top, GetWidth(), GetHeight());
ReleaseDC(m_hWnd, hDC);
}
// 切換至下一幀
SelectActiveFrame(&pageGuid, m_nFramePosition++);
if (m_nFramePosition == m_nFrameCount) m_nFramePosition = 0; // 循環
// 取得當前幀延遲 (毫秒)
long lPause = ((long*)m_pPropertyItem->value)[m_nFramePosition] * 10;
// 等待延遲或退出事件
DWORD dwErr = WaitForSingleObject(m_hExitEvent, lPause);
return dwErr == WAIT_OBJECT_0; // true = 請求退出
}
void Play() { ResetEvent(m_hPause); CreateThread(..., ThreadProc, this); }
void Pause() { SetEvent(m_hPause); }
void Stop() { SetEvent(m_hExitEvent); SetEvent(m_hPause); }
// 視窗 OnPaint
void CMyWnd::OnPaint() {
CPaintDC dc(this);
Graphics g(dc);
if (m_img.TestForAnimatedGIF()) {
m_img.Play(); // 自動在背景執行緒播放
} else {
g.DrawImage(&m_img, 0, 0, m_img.GetWidth(), m_img.GetHeight());
}
}
// 視窗銷毀
void CMyWnd::OnDestroy() {
m_img.Stop();
CWnd::OnDestroy();
}

SelectActiveFrame 會修改 Image 內部狀態,**非執行緒安全**。若多處同時繪製同一 ImageEx 實例會導致幀序混亂。解決:每執行緒/視窗持有獨立實例,或加入關鍵區同步。PropertyTagFrameDelay 單位為 1/100 秒,乘 10 轉毫秒。部分 GIF 延遲為 0,建議下限 50ms 避免 CPU 佔用過高。| 方案 | 優點 | 缺點 |
|---|---|---|
| GDI+ ImageEx (本文) | 程式碼少、無外部相依、整合 GDI+ 繪圖管線 | 處置方式支援不全、執行緒不安全 |
| Windows Imaging Component (WIC) | 系統級、完整 GIF 規範支援、多執行緒安全 | API 複雜、需 Vista+、程式碼量大 |
| 第三方庫 | 功能完整 (APNG、WebP)、高效能 | 增加相依、授權考量 |
利用 GDI+ 內建 FrameDimensionTime、PropertyTagFrameDelay、SelectActiveFrame,配合背景執行緒即可實作動態 GIF 播放器。ImageEx 封裝降低使用門檻,適合快速整合至 MFC/WinForms/WPF (透過 Interop) 專案。完整原始碼含 ImageEx.h/cpp、測試對話框專案,請下載附件。