vc6.0中用GDIPlus實現載入動態gif圖片(非MFC實現)

來源:互聯網
上載者:User

 

今天心情很嗨皮,原因是花了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的訊息吧)。最後歡迎大家交流學習。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.