VS2010下用GDIplus(GDI+)在控制檯視窗顯示多種格式的圖片(二)
GDI+ 處理gif動畫檔案
這裡,我們將在控制檯視窗讀入gif動畫影象,並顯示出來。
原理
-
GDI+的Image物件提供了直接對多頁GIF、TIF檔案格式的支援。多頁影象是指影象中包含有多個圖形頁。每頁可以看作影象幀。這些影象幀通過連續的顯示就形成了一副動畫。
-
呼叫Image物件的成員函式
GetFrameDimensionsCount
可以得到Image物件的Dimension
數。每個Dimension
通過一個GUID
標示。
【注】(GUID,Globally Unique Identifier)全域性唯一識別符號是一種由演算法生成的二進位制長度為128位的數字識別符號。在 Windows 平臺上,GUID 廣泛應用於微軟的產品中,用於標識如登錄檔項、類及介面標識、資料庫、系統目錄等物件。
-
函式GetFrameDimensionsList可以返回所有Dimension的GUID值。第一個GUID值儲存在函式引數pDimensionsIDs陣列的索引0處。
-
GetFrameCount可以得到每個Dimension裡有多少個Frame。
簡單示例程式碼段:
Image* image = new Image(L"Multiframe.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檔案都是含有多幀的,所以我們在顯示GIF的時候可以通過上面的程式碼根據frameCount的值判斷這個GIF檔案是否有多個幀。
在確認有多個幀的影象以後,還要得到每幀影象顯示的間隔時間。
GDI+的Image物件提供了 GetPropertyItem獲取影象的屬性。GetPropertyItem函式需要使用者傳遞資料返回緩衝區和大小。所以在使用前先用GetPropertyItemSize得到需要的緩衝區大小,分配空間後再取得屬性資料。
//PropertyTagFrameDelay是GDI+中預定義的一個GIF屬性ID值,表示標籤幀資料的延遲時間
int size = GetPropertySize(PropertyTagFrameDelay);
PropertyItem* pItem = NULL;
pItem = (PropertyItem*)malloc(size);
image->GetPropertyItem(PropertyTagFrameDelay,size,pItem);
這樣就把所有和PropertyTagFrameDelay屬性相關的資料取到了pItem中。然後通過pItem訪問結構中的value。
每兩幀影象之間的間隔時間是不一定相同的,所以還需要得到當前正顯示的幀影象的索引值。最後呼叫Image物件的DrawImage函式把每幀影象畫出來。
接下來的簡單程式碼段如下:
int fcount=0;
//Guid的值在顯示GIF為FrameDimensionTime,顯示TIF時為FrameDimensionPage
GUID Guid = FrameDimensionTime;
while(true)
{
Graphics gh(hDC); //hDC是外部傳入的畫圖DC
gh.DrawImage(image,0,0,image->GetWidth(),image->GetHeight());
//重新設定當前的活動資料幀
image->SelectActiveFrame(&Guid,fcount++);
if(fcount == frameCount) //frameCount是上面GetFrameCount返回值
fcount= 0; //如果到了最後一幀資料又重新開始
//計算此幀要延遲的時間
long lPause = ((long*)pItem->value)[fcount]*10;
Sleep(lPause); //lPause為幀間時延(毫秒)
}
例項 (VS2010編譯)
簡化程式碼(38行,讀取指定gif檔案)
#include<Windows.h>
#include<gdiplus.h>
#define ULONG_PTR ULONG
#pragma comment(lib,"gdiplus.lib")
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow ();
using namespace Gdiplus;
void main()
{
GdiplusStartupInput m_gdiplusStartupInput;
ULONG_PTR m_gdiplusToken;
GdiplusStartup(&m_gdiplusToken, &m_gdiplusStartupInput, NULL);
HWND hWnd = GetConsoleWindow();
HDC hdc = GetDC(hWnd);
Image* pImage = new Image(L"e1.gif");//讀gif檔案
UINT nCount = pImage->GetFrameDimensionsCount();
GUID* pDimensionsIDs = (GUID*)new GUID[nCount];
pImage->GetFrameDimensionsList(pDimensionsIDs,nCount);
UINT nFrameCount = pImage->GetFrameCount(&pDimensionsIDs[0]);
int size = pImage->GetPropertyItemSize(PropertyTagFrameDelay);
byte* p = new byte[size];
PropertyItem* pItem = (PropertyItem*)p;
pImage->GetPropertyItem(PropertyTagFrameDelay,size, pItem);
UINT fcount = 0;
GUID guid = FrameDimensionTime;
while(TRUE)
{
Graphics gc(hdc);
gc.DrawImage(pImage,0,0,pImage->GetWidth(),pImage->GetHeight());
pImage->SelectActiveFrame(&guid,fcount++);//讀取下一幀
if (fcount >= nFrameCount){fcount = 0;}//如果到了最後一幀資料又重新開始
long pause = ((long*)pItem->value)[fcount]*10;//計算此幀要延遲的時間
Sleep(pause);
}
}
【注】以上程式碼在VS2010下編譯成功。在VC6.0下,在使用VS2010所帶的gdiplus.lib
(2009年12月版本)方可。可將VC6中...\VC6\SDK\Masm32\lib
中的老版gdiplus.lib
(2003年3月版本)替換掉。並用Release模式編譯,可通過。
改進程式碼(90行)
可讀取(.gif .jpg .png .wmf .emf …)等格式圖片。ESC退出。在VS2010或VC6.0(Release模式)下編譯成功。
#include<Windows.h>
#include<stdio.h>
#include<conio.h>
#include<gdiplus.h>
#define ULONG_PTR ULONG
#pragma comment(lib,"gdiplus.lib")
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow ();
using namespace Gdiplus;
WCHAR * charToWCHAR(char *s) {
int w_nlen = MultiByteToWideChar(CP_ACP, 0, s, -1, NULL, 0);
WCHAR *ret;
ret = (WCHAR*)malloc(sizeof(WCHAR)*w_nlen);
memset(ret, 0, sizeof(ret));
MultiByteToWideChar(CP_ACP, 0, s, -1, ret, w_nlen);
return ret;
}
int main(int argc, char *argv[])
{
GdiplusStartupInput m_gdiplusStartupInput;
ULONG_PTR m_gdiplusToken;
GdiplusStartup(&m_gdiplusToken, &m_gdiplusStartupInput, NULL);
WCHAR * filename;
if(argc>1){
filename =charToWCHAR(argv[1]);
}else
{
printf("Usage: picview filename.XXX (.gif .jpg .png .wmf .emf ...)\n");
printf("ESC to Exit\n");
return FALSE;
}
HWND hWnd = GetConsoleWindow();
HDC hdc = GetDC(hWnd);
Image* pImage = new Image(filename);
if ((pImage==NULL)||(pImage->GetLastStatus()!=Ok))
{
if (pImage)
{
printf("無法開啟圖片");
delete pImage;
pImage = NULL;
}
return FALSE;
}
free(filename);//用完即刪掉
UINT nCount = pImage->GetFrameDimensionsCount();
GUID* pDimensionsIDs = (GUID*)new GUID[nCount];
pImage->GetFrameDimensionsList(pDimensionsIDs,nCount);
//返回有關多幀影象的資訊。
UINT nFrameCount = pImage->GetFrameCount(&pDimensionsIDs[0]);
delete[] pDimensionsIDs;//釋放
int size = pImage->GetPropertyItemSize(PropertyTagFrameDelay);
byte* p = new byte[size];
PropertyItem* pItem = (PropertyItem*)p;
pImage->GetPropertyItem(PropertyTagFrameDelay,size, pItem);
UINT fcount = 0;
GUID guid = FrameDimensionTime;
while(TRUE)
{
Graphics gc(hdc);
gc.DrawImage(pImage,40,40,pImage->GetWidth(),pImage->GetHeight());
if(nFrameCount>1){
pImage->SelectActiveFrame(&guid,fcount++);//讀取下一幀
if (fcount >= nFrameCount){fcount = 0;}//如果到了最後一幀資料又重新開始
long pause = ((long*)pItem->value)[fcount]*10;//計算此幀要延遲的時間
Sleep(pause);
}
else
{
Sleep(100);
}
if (_kbhit())//檢查是否有按鍵按下
{
if (_getch() == 0x1b){break;}//若按下ESC鍵跳出迴圈
}
}
system("cls");
return 0;
}
【注1】這個程式是用GDI+ 直接在螢幕區作圖的,沒有使用雙緩衝技術。語句Graphics gc(hdc);
直接建立在螢幕區hdc
上。
【注2】如將語句Graphics gc(hdc);
置於while迴圈之前,程式能正常執行,但用ESC退出時會有問題,以Ctrl+C方可,原因暫還不明。
【注3】較嚴謹的做法是:在程式結束前可釋放GDI+ 資源,如:
delete pImage;//delete用於釋放new產生的物件
pImage=NULL;
ReleaseDC(hWnd,hdc);
GdiplusShutdown(m_gdiplusToken );//釋放GDI+
執行結果
測試上各種格式的圖片。如下:
GDI+ 應用步驟小結
1、包括GDI+ 相應的標頭檔案及引入相應的lib
#include <GdiPlus.h>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
2、初始化Gdiplus
Gdiplus::GdiplusStartupInput m_gdiplusStartupInput;
ULONG_PTR m_gdiplusToken;
GdiplusStartup( &m_gdiplusToken, &m_gdiplusStartupInput, NULL );
3、載入相應的資源(如圖片)
Image* m_pImage;//圖片物件
m_pImage=Image::FromFile(_T("Test03.jpg"));
//錯誤判斷
if ((m_pImage==NULL)||(m_pImage->GetLastStatus()!=Ok))
{
if ( m_pImage )
{
delete m_pImage;
m_pImage = NULL;
}
return FALSE;
}
在第4步用完後可釋放m_pImage
。
4、繪製圖片
Graphics graphics(hdc); //hdc為顯示區或緩衝區控制代碼
graphics.DrawImage(m_pImage, 0,0,m_pImage->GetWidth(),m_pImage->GetWidth());
【注】如果是gif檔案有多幀影象,或需反覆重繪更新,可用while語句與Sleep函式配合,也可做多執行緒定時激發。
5、釋放資源,關閉Gdiplus
delete m_pImage;
m_pImage=NULL;
GdiplusShutdown( m_gdiplusToken );
【注】這步在簡單控制檯程式中也可省掉。