1. 程式人生 > >VS2010下用GDIplus(GDI+)在控制檯視窗顯示多種格式的圖片(二)

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 );

【注】這步在簡單控制檯程式中也可省掉。