惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
N
Netflix TechBlog - Medium
宝玉的分享
宝玉的分享
Google DeepMind News
Google DeepMind News
腾讯CDC
G
Google Developers Blog
Martin Fowler
Martin Fowler
Microsoft Security Blog
Microsoft Security Blog
Recent Announcements
Recent Announcements
爱范儿
爱范儿
Engineering at Meta
Engineering at Meta
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
有赞技术团队
有赞技术团队
Jina AI
Jina AI
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
M
MIT News - Artificial intelligence
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
美团技术团队
WordPress大学
WordPress大学
阮一峰的网络日志
阮一峰的网络日志

博客园 - domini

给年轻工程师的十大忠告 vc定时以及计时的方法 数字图像处理常用词 VC里一些容易混淆的地方 VC数据类型的转换 C语言 第二讲 数据类型 windows进程中的内存结构 调试方法和技巧,作者:非凡 Bitblt的参数 图像的文件格式bmp gif 怎样在内存缓冲中画图 转载 原作者 相生昌 MFC GDI双缓冲避免图形闪烁,转载 关键字 Windows API函数大全 利用GDI+的双缓冲技术来提高绘图效率 屏幕任意抓图截图 Visual C++的程序设计技巧 用Visua C++6.0为灰度图像增加彩色滤镜制作"复古照" 模板匹配 API函数大全
VC中用GDI函数实规高速平滑动画
domini · 2005-03-22 · via 博客园 - domini


摘要:许多游戏软件的开发中,实现高速平滑的动画需要许多比较深的技术,如:OpenGL、DirectX,并且可能还要开发人员有深厚的数学功底。但是,我们在开发一些小游戏,或为应用程序的界面实现一些动画效果,就可能不用以上这些技术了,我们更多的是用Windows API提供或MFC封装后的GDI绘图函数来实现。为此我们可 不可以用GDI来实现高速平滑的动画呢?答案是肯定的本文教您如何用GDI函数来开发平滑无闪的动画,并以一个应用实例来介绍这些用法。

       关键词:GDI,MFC,Bitmap,内存设备环境

一、动画原理。

大家都知道播放电影的原理:在规定时间(一般为1秒)播放24幅连续的画面,由于人的视觉暂留,所以人们在观看电影时,看到的就不是一幅一幅的画面,而是丰富精彩的场景。于是,我们也模仿电影播放原理来用在编程中实现平滑无闪的动画。其实,这个原理已经在当今动画技术中实现,但我们讨论的是在VC++中用GDI函数实现同样效果的技术。

我已采用此技术开发了一款纸牌游戏:“扑克麻将”。读者可到Http://www.csdn.net程序员大本营上下载,软件代号:9175。“扑克麻将”中使用的动画技术全部系文本介绍,从游戏中读者可看到:不论是发牌,出牌,选牌,吃牌等各种操作,游戏画面看不到一丝闪动,速动也极快。

二、实现方法。

其实本技术也很简单,其关键就是在内存中创建一个与显示动画的窗口区域一样大的位图,先用GDI函数绘制位图,然后在适当的时候从内存中显示出来。因为位图已经绘制好,不象平时编程一样边绘制边显示,所以,显示一帧图形时,减速少了闪烁,从而实现平滑动画;并且,图形是从内存中直接显示到当前窗体,所以速度也很快,从而实现高速动画。下面我们将介绍实现这些技术的步骤:

1、启动 VC++,创建一个MFC支持的单文档应用程序。

2、选择菜单项Inert\New Class创建一个从CBitmap类继承的类,取名为:CMemBitmap。我们创建了一个位图类来模仿电影中的一帧画面,作为将要显示在窗口区域(电影屏幕)的图像。今后,所有的绘图操作都针对这个位图类进行,而这些绘图操作,我们可以用成员函数来实现,比如:显示一个位图、一段文字及GDI函数中所有的绘图函数。

3、创建好位图类后,为了同窗体联系起来,用窗体的CDC内存设备环境指针创建该位图与窗体的客户区一样大。为此在位图类头文件MemBitmap.h可声明一个CWnd指针成员变量:m_PWnd,用以指向窗体,如下代码所示:

private:
     CWnd* pWnd;
再声明一个成员函数来创建位图,其声明代码如下所示:
public:
void        Init(CWnd* pwnd);
在MemBitmap.cpp中实现代码如下:

//初始化位图类
void CMemBitmap::init(CWnd *pwnd)
{
   RECT      rt;              //保存窗体客户区域的大小的矩形类型变量
   pWnd = pwnd;                                   //获取窗体指针
   pwnd->GetClientRect(&rt);         //得到窗体客户区域的大小
   //利用窗体类的CDC指针在内存中创建位图
   CreateCompatibleBitmap(pwnd->GetDC(), rt.right;, rt.bottom);
}
CreateCompatibleBitmap函数作用是初始化位图类,其原型如下:
BOOL CreateCompatibleBitmap( CDC* pDC, int nWidth, int nHeight );
pDC是设备环境指针,本例用窗体的设备环境指针。nWidth和nHeight是指定该位图尺寸的高度与宽度,单位为象素。

4、添加成员函数完成绘图功能。为了能在动画中显示文本信息,我们添加一个成员变更来显示文本信息。其原型的代码如下:
//MemBitmap.h文件中
public:
void TextOut(int x, int y, int iSize, LPCSTR strText,COLORREF color);
//MemBitmap.cpp文件中
void CMemBitmap::TextOut(int x, int y, int iSize, LPCSTR strText, COLORREF color)
{
       CDC*       pDC = pWnd->GetDC();//获取窗体的指针
       CFont       NewFont;   //文本的字体对象
       CFont       *OldFont; //保存以前的字体指针
       CDC       dcMem ;         //内存中的DC指针,以便调用GDI函数在位图中绘图
       dcMem.CreateCompatibleDC(pDC);    //创建与窗体设备环境一样大小DC
       dcMem.SelectObject(this);       //将内存中的DC选择该类的位图对象
       NewFont.CreatePointFont(iSize,"宋体");//创建显示文本的字体
       OldFont = dcMem.SelectObject(&NewFont);     //选择新字体
       dcMem.SetTextColor(color);
       dcMem.TextOut(x,y,message);//在指定位置显示文本
       dcMem.SelectObject(OldFont);
       //释放
       NewFont.DeleteObject();
       dcMem.DeleteDC();
       pWnd->ReleaseDC(pDC);
}

TextOut函数用于在指定位置用指定的大小,颜色显示文本。参数x,y分别是显示文本的位置,iSize指定文本字体的大小,color指定文本的颜色,strText指定要显示的内容。从以上代码中,用一个内存设备环境dcMem来显示文本:首先从窗体设备环境创建,再选择该位图类,之后,即可用CDC类的绘图函数进行绘图了。同样,读者可、以用该内存设备环境变量dcMem来绘制一个位图(从文件或资源来的)、画直线等所有GDI函数的操作,而我们添加函数功能在于将这些GDI函数进行封装,以便调用方便,这也是面向对象编程的思想。

5、我们再实现一个清位图函数,以便在适当时候用指定的颜色将位图填充,达到清图的效果,其代码如下:
//清除位图的一个区

void CMemBitmap::Clear(int x1, int y1, int x2, int y2, COLORREF color)
{
       CDC*       pDC = m_pWnd->GetDC();
       CDC       dcMem ;              //内存中的DC指针
      dcMem.CreateCompatibleDC(pDC);    
      dcMem.SelectObject(this);       
       CBrush  *OldBrush , blbrush(color);
       dcMem.SetBkMode( TRANSPARENT );
       dcMem.SetBkColor(color);
       OldBrush = dcMem.SelectObject( &blbrush );
       dcMem.Rectangle( x1 , y1 , x2 , y2 );       
       dcMem.SelectObject(OldBrush);
       dcMem.DeleteDC();
       m_pWnd->ReleaseDC(pDC);
}

参数x1,y1,x2,y2指定了矩形区的尺寸,color指定了填充色。其实现方法与4中所述一样,在此不必多介绍。
6、添加了绘图函数,下面再介绍如何使用CMemBitmap类,来实现动画效果:
首先,我们在视图类(也可以是其它窗口类)中声明一个CMemBitmap成员变量m_MemBitmap,代码如下:

private:
    CMemBitmap m_MemBitmap;

然后,我们重载Cview类函数OnInitialUpdate(),以便视图初始化结束后初始化位图对象,并且视图指针传递过去,其实现代码如下:

void CTestBitmapView::OnInitialUpdate()

{
    CView::OnInitialUpdate();
    // TODO: Add your specialized code here and/or call the base class
    m_MemBitmap.init(this);
    SetTimer(1,100,NULL);    
}  

在函数最后,启动了一个定时器, 我们将用定时来实现动画功能。

接着,我们重载定时器消息函数OnTimer实现动画功能。其实现代码如下:

void CTestBitmapView::OnTimer(UINT nIDEvent)

{
    int        x , y;//文本显示的位置
    CRect    rect;//客户区域
    CDC*    pDC = GetDC();//获取视图的DC
    CDC    dcComp;
    //得到客户区尺寸
    GetClientRect(&rect);
    //随机获得要显示文本的位置
    srand( (unsigned)time( NULL ) );
    //控制文本显示的位置位于客户区以内
    x = rand()%rect.Width()/2;
    y = rand()%rect.Height();  
    //在内存中显示文本   
    m_MemBitmap.Clear(rect.left,rect.top,rect.right,rect.bottom,RGB(0,0,0));
    m_MemBitmap.TextOut(10,10,500,"固定的文本",RGB(255,255,255));
    m_MemBitmap.TextOut(x,y,400,"GDI函数实现高速动画演示",RGB(255,255,0));
    //内存设备环境将位图对象选入
    dcComp.CreateCompatibleDC(pDC);
    dcComp.SelectObject(&m_MemBitmap);
    //用位传输函数显示出来
    pDC->BitBlt(0,0,rect.Width(),rect.Height(), &dcComp, 0,0,SRCCOPY);
    dcComp.DeleteDC();
    ReleaseDC(pDC);
    CView::OnTimer(nIDEvent);
}

       读者可根据注释理解代码的含义,需要说明的是:在内存中绘制位图时,本例采用了一个静态显示文本和一个随机动态显示的文本来比较,从运行情况可以看出动态显示的文本0.1秒就随机移动一次位置,虽然每次绘制位图都使用Clear函数清屏,但静态文本的显示没有一点闪动,动画非常平滑,速度也很快。

怎么样,很简单吧?如果您再创建一个线程后台绘制图形,将会实现很多特殊效果的动画来,我们可以将这项技术用在用户界面上或其他地方,将会收到意想不到的动画效果。

三、结束语

通过以例子,用GDI函数实现高速平滑的动画也很简单。我们可以添加绘制位图,画线、画矩形等成员函数,便能实现各种GDI绘图函数的操作,如果读者还有兴趣,可以在显示位图,作优化显示,如:不是将位图全部显示出来,而是显示其中动画的一部分,因为BitBlt函数作位传输很慢。我开发的纸牌游戏“扑克麻将”就经过优化,其动画速很快,如果读者有兴趣,请到程序员大本营(http://www.csdn.net )共享软件栏目下载。欢迎来信与我切磋VC++编程技巧