VS2010下用GDIplus(GDI+)在控制台窗口显示多种格式的图片(二)

文章目录

    • GDI+ 处理gif动画文件
      • 原理
    • 实例 (VS2010编译)
      • 简化代码(38行,读取指定gif文件)
      • 改进代码(90行)
    • 运行结果
    • 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
#include
#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
#include
#include
#include
#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+

运行结果

测试上各种格式的图片。如下:
VS2010下用GDIplus(GDI+)在控制台窗口显示多种格式的图片(二)_第1张图片

GDI+ 应用步骤小结

1、包括GDI+ 相应的头文件及引入相应的lib

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

【注】这步在简单控制台程序中也可省掉。

你可能感兴趣的:(C语言开发应用)