IPicture COM 介面繪製靜態圖片(支援 GIF 首幀)IPicture::Render 形成動畫
IPicture 介面基本用法get_Width/get_Height:取得圖片尺寸(HIMETRIC 單位)Render(hDC, x, y, w, h, srcX, srcY, srcW, srcH, rc):繪製到指定 DCOleLoadPicture(IStream*, size, FALSE, IID_IPicture, (void**)&pic)每幀以 0x2C 開頭(前一位元組必為 0x00),後接 GifImage 結構:
typedef struct {
WORD logX, logY, width, height;
struct { BYTE d:3, c:1, b:3, a:1; } Flag; // a=1 表示有區域調色盤
} GifImage;
資料區塊:長度位元組 + 資料 重複,以 0x00 結束。
// 1. 從資源載入 GIF 原始資料
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(IDR_GIF), "IMAGE");
DWORD size = SizeofResource(hInst, hRes);
BYTE* pSrc = (BYTE*)LoadResource(hInst, hRes);
// 2. 掃描所有幀位置與大小
BYTE* frames[20]; DWORD frameSize[20]; int frameCnt = 0;
DWORD firstPos = 0;
for (DWORD i = 0; i < size; i++) {
if (pSrc[i] == 0x2C && pSrc[i-1] == 0x00) {
GifImage* gif = (GifImage*)&pSrc[i+1];
DWORD offset = 1 + sizeof(GifImage);
if (gif->Flag.a) offset += 3 * (1 << (gif->Flag.d + 1)); // 區域調色盤
while (pSrc[i+offset] != 0) offset += pSrc[i+offset] + 1;
offset++; // 含結束 0x00
if (frameCnt == 0) firstPos = i;
frames[frameCnt] = new BYTE[offset];
memcpy(frames[frameCnt], &pSrc[i], offset);
frameSize[frameCnt] = offset;
i += offset - 1;
frameCnt++;
}
}
// 3. 播放迴圈(建議放在獨立執行緒)
while (playing) {
for (int f = 0; f < frameCnt; f++) {
// 覆蓋首幀資料
DWORD oldProtect;
VirtualProtect(pSrc, size, PAGE_READWRITE, &oldProtect);
memcpy(&pSrc[firstPos], frames[f], frameSize[f]);
VirtualProtect(pSrc, size, oldProtect, NULL);
// 建立串流並載入 IPicture
CMemFile mem(pSrc, size);
CArchive ar(&mem, CArchive::load | CArchive::bNoFlushOnDelete);
CArchiveStream stream(&ar);
CComQIPtr pic;
OleLoadPicture((LPSTREAM)&stream, 0, FALSE, IID_IPicture, (void**)&pic);
// 繪製
long w, h; pic->get_Width(&w); pic->get_Height(&h);
CSize sz(w, h); pDC->HIMETRICtoDP(&sz);
CRect rc(0,0,sz.cx,sz.cy);
pic->Render(*pDC, 0,0,sz.cx,sz.cy, 0,h,w,-h, &rc);
Sleep(100); // 幀延遲,可依 GIF 內建延遲調整
}
}
Sleep(100) 簡化VirtualProtect 必須成對呼叫