今天心情很嗨皮,原因是花了5塊錢買了一張刮刮彩,給中了100快,喜悅感可謂是油然而生吧,好了進入正題吧。關於GDI+的介紹在這裡就不談了,總之相比GDI確實方便了太多,比如首先GDI僅僅支援bmp格式的位元影像,對其他格式比如jpg,png,gif.....都很吃力了,而用GDI+確全部支援,而且用起來十分順手,比如放置一幅jpg圖片:
Image image(L"Texture1.jpg");graphics.DrawImage(&image, 0, 0, image.GetWidth(), image.GetHeight());
當然還需要構造一個graphics對象,也很方便:
Graphics graphics(hdc);
Microsoft Windows GDI+包含大約40個類、50個枚舉和6個結構體。同時也有少數幾個函數不屬於任何類。Graphics類是整個GDI+介面的核心;它是實際進行線條、曲線、圖形、圖象和文本繪製的類。
多數類和Graphics類配合使用。例如,Graphics::DrawLine方法接受一個點傳給一個Pen對象,該對象儲存了即將繪製的線條的屬性(顏色、寬度、虛線類型及其他)。Graphics::FillRectangle方法接受一個點傳給LinearGradientBrush對象,該對象協同Graphics對象實現矩形的漸層色填充。Font和StringFormat對象影響到Graphics對象繪製文本的方式。Matrix對象用於儲存和產生一個Graphics對象的世界變換矩陣,用於旋轉、縮放和翻轉圖象。
至於想要知道更具體的就去找你度娘跟Google吧,初次接觸GDI+讓我興奮的是他的線性漸層跟路徑漸層,映像旋轉映像平移,用這些弄出來的畫面十分好看的,而且操作也十分容易。。。。。。。拍馬屁的話不多說了,也不是我的風格,大家接觸了就明了了。
說到支援gif,我趕緊就去測試了一把,就把上面代碼中的jpg換做gif就OK了,如果gif是靜態,那完全滿足要求,如果是動態,那運行結果有點失望,現實的是靜態,說準確點就是該gif圖片的第一個幀,哪如何載入動態gif呢,首先想到的是分析gif格式,網上也能夠找到一大堆資料,不過看著還蠻費勁的,看過之後分析,知道gif實際上是有很多歌幀組成的,我們可以一個接一個的來顯示出來,從而整體看起來就是一個符合要求的結果。
首先判斷gif有多少個幀,用到如下幾個API:
Image *image=new Image(L"123.gif"); UINT count=0; count=image->GetFrameDimensionsCount(); GUID *pDimensionIDs=(GUID*)new GUID[count]; image->GetFrameDimensionsList(pDimensionIDs,count); WCHAR strGuid[39]; StringFromGUID2(pDimensionIDs[0],strGuid,39); UINT frameCount=image->GetFrameCount(&pDimensionIDs[0]); delete []pDimensionIDs;
得到幀的數量之後,其實根據數量我們也能判斷出來這個gif是否是動態,是否大於1就是了,數量判斷完畢接著就該判斷幀的時間了吧,所謂的延遲。
// 假設映像具有屬性條目 PropertyItemEquipMake. // 擷取此條目的大小. int nSize = GetPropertyItemSize(PropertyTagFrameDelay); // 為屬性條目分配空間. m_pPropertyItem = (PropertyItem*) malloc(nSize); GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem); m_pPropertyItem->value 是一個長整形數組, 每個長整形代表每幀的延時。由於擷取的屬性不同,GetPropertyItem會獲得不同大小的對象, 因此要由使用者來獲得的對象大小,開闢與刪除 GetPropertyItem相關的記憶體。對象的大小是通過GetPropertyItemSize 擷取的,其參數是你所感興趣的屬性條目。 一旦擷取了幀的數量與延時,我們就可產生一個線程來調用 DrawFrameGIF()來顯示。
起初我沒有用到線程,我認為用線程不好控制(個人對線程的應用少之又少),反倒是寫了一個單獨的函數放到了WM_PAINT中,大概是這樣的:
void showimage(HDC hdc){Image *image=new Image(L"123.gif"); UINT count=0; count=image->GetFrameDimensionsCount(); GUID *pDimensionIDs=(GUID*)new GUID[count]; image->GetFrameDimensionsList(pDimensionIDs,count); WCHAR strGuid[39]; StringFromGUID2(pDimensionIDs[0],strGuid,39); UINT frameCount=image->GetFrameCount(&pDimensionIDs[0]); delete []pDimensionIDs; int size=image->GetPropertyItemSize(PropertyTagFrameDelay); PropertyItem* pItem=NULL; pItem=(PropertyItem*)malloc(size); image->GetPropertyItem(PropertyTagFrameDelay,size,pItem); UINT fcount=0; GUID Guid=FrameDimensionTime; while(true) { Graphics graphics(hdc); graphics.DrawImage(image,0,0,image->GetWidth(),image->GetHeight()); image->SelectActiveFrame(&Guid,fcount++); if(fcount==frameCount) fcount=0; long lPause=((long*)pItem->value)[fcount]*10; Sleep(lPause); }}
放到了WM_PAINT訊息中,但是運行發現動態gif確實顯示出來了,但是當程式視窗失去焦點進而發生重繪的時候發現圖片就不會動了,最小最大化也是如此,可能就是那個死迴圈從中作怪吧,此時又想到了多線程來完成,於是改動了一下代碼,應該是這樣的:
void __cdecl showimage(LPVOID){ HDC hdc=GetDC(g_hWnd); Image *image=new Image(L"123.gif"); UINT count=0; count=image->GetFrameDimensionsCount(); GUID *pDimensionIDs=(GUID*)new GUID[count]; image->GetFrameDimensionsList(pDimensionIDs,count); WCHAR strGuid[39]; StringFromGUID2(pDimensionIDs[0],strGuid,39); UINT frameCount=image->GetFrameCount(&pDimensionIDs[0]); delete []pDimensionIDs; int size=image->GetPropertyItemSize(PropertyTagFrameDelay); PropertyItem* pItem=NULL; pItem=(PropertyItem*)malloc(size); image->GetPropertyItem(PropertyTagFrameDelay,size,pItem); UINT fcount=0; GUID Guid=FrameDimensionTime; while(true) { Graphics graphics(hdc); graphics.DrawImage(image,0,0,image->GetWidth(),image->GetHeight()); image->SelectActiveFrame(&Guid,fcount++); if(fcount==frameCount) fcount=0; long lPause=((long*)pItem->value)[fcount]*10; Sleep(lPause); } ReleaseDC(g_hWnd,hdc);}
case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: Add any drawing code here... RECT rt; GetClientRect(hWnd, &rt); _beginthread(showimage,0,0); //showimage(hdc); EndPaint(hWnd, &ps); break;
運行發現此時上述的那個重繪時遇到的圖片假死已經不複存在了,這個時候表明看起來沒什麼問題了,但是仔細看發現gif圖片交換的頻率好像快了不少(跟原來的123.gif進行了肉眼對比),顯的跟原版圖片還是有點差距的,總之是不太理想,原因大致查了一下,可能是線程還沒執行玩就 EndPaint(hWnd, &ps);了
最終跟網友交流方法,說到了定時器的思路,大體一想,確實定時器可以做到,思路:
開一個定時器。然後取gif當前幀到下一幀的時間間隔,做為定時器的間隔(最小100ms)。(定時器只用一次)。每次定時器到了。就把活動幀設到下一幀,然後觸發視窗重繪。
剩下的。你只需要在wm_paint裡面,把gif的當前幀畫出來就好了.
大概流程:
1.取幀間隔,開定時器。
2.定時器到。殺掉定時器,選中下一幀,再取幀間隔。
3.觸發視窗重繪。
按照這個思路終於算是比較完美的實現了gif動態圖片的載入,主要代碼大致這樣的:
//定義2個ID#define TIMER_FIR 1#define TIMER_SEC 2//核心工作case WM_CREATE: SetTimer(hWnd,TIMER_FIR,0,NULL); break; case WM_TIMER: { switch(wParam) { case TIMER_FIR: { hdc=GetDC(hWnd); image=new Image(L"123.gif"); count=0; count=image->GetFrameDimensionsCount();pDimensionIDs=(GUID*)new GUID[count]; image->GetFrameDimensionsList(pDimensionIDs,count); StringFromGUID2(pDimensionIDs[0],strGuid,39);frameCount=image->GetFrameCount(&pDimensionIDs[0]); delete []pDimensionIDs; size=image->GetPropertyItemSize(PropertyTagFrameDelay); //PropertyItem* pItem=NULL; // pItem=(PropertyItem*)malloc(size); pItem=(PropertyItem*)new PropertyItem[size]; image->GetPropertyItem(PropertyTagFrameDelay,size,pItem);fcount=0;Guid=FrameDimensionTime;Graphics graphics(hdc); graphics.DrawImage(image,0,0); image->SelectActiveFrame(&Guid,fcount++); if(fcount==frameCount) fcount=0; lPause=((long*)pItem->value)[fcount]*10; //ReleaseDC(hWnd,hdc); KillTimer (hWnd, TIMER_FIR) ; SetTimer(hWnd,TIMER_SEC,lPause,NULL); InvalidateRect (hWnd, NULL, FALSE) ; break; } case TIMER_SEC: { image->SelectActiveFrame(&Guid,fcount++); if(fcount==frameCount) fcount=0; lPause=((long*)pItem->value)[fcount]*10; KillTimer(hWnd,TIMER_SEC); SetTimer(hWnd,TIMER_SEC,lPause,NULL); InvalidateRect (hWnd, NULL, FALSE) ; } } }
主要是分享一下這個問題的解決思路,免得後面的人又繞彎路,這樣就不太好了。
貼一張運行:
完整的工程已經上傳到了CSDN資源區了,但是不曉得什麼原因現在還找不到地址,稍後會附上附近的(等CSDN的訊息吧)。最後歡迎大家交流學習。