织梦CMS - 轻松建站从此开始!

罗索

浅谈位图的淡入淡出和渐隐,位图操作

jackyhwei 发布于 2010-05-31 14:09 点击:次 
在许多游戏和屏幕保护程序中,我们都可以发现位图的淡入淡出和渐隐(一幅图象渐渐的消失于另一幅图象中)的应用。如何实现这些效果呢?
TAG:

在许多游戏和屏幕保护程序中,我们都可以发现位图的淡入淡出和渐隐(一幅图象渐渐的消失于另一幅图象中)的应用。如何实现这些效果呢?

在windows(GDI)环境下,实现位图的淡入淡出和渐隐的方法有三种:1.调色板动画;2.模式画刷;3.动画法。其中,第一种方法速度很快,但只能用于256色的图形,而且不易实现渐隐效果。第二种方法实现比较简单,但是主观效果不及其余两种。第三种方法的效果很好,但速度要稍慢一些。由于现在已经很难得到质量较高的256色图片,加之目前几乎所有的显卡均支持高彩和真彩模式,所以不推荐采用第一种方法。下面介绍后两种方法在Visual C++编程环境下的实现。

一:模式画刷法:
      CDC类的BitBlt(...),MaskBlt(...),以及WIN32API ::StretchDIBits(...)函数均支持三元ROP(Raster Operation)操作,即由源,模式画刷(pattern brush)和目的区域原有的图形经一定的逻辑运算而形成最终的输出图形。所以,通过改变模式画刷的图案,辅以一定的ROP操作,就可以形成一些特殊的效果。
     首先,要准备若干个8*8的单色位图,作为模式画刷的模板。单色位图中应只含有黑白两种颜色的像素,每一个位图中两种像素的比例和形状将决定显示的效果,通常我们由一个全黑的位图开始,逐渐增加白色像素的比例,最后一幅位图全部由白色象素组成。
     这些位图制作好以后,将它们Import入工程,命名为IDB_PATTERN1、IDB_PATTERN2 ... ... 调用CBitmap::LoadBitmap(...)函数将其选入对应的CBitmap对象,然后调用CBrush::CreatePatternBrush(...)制作模式画刷。
    有了合适的模式画刷以后,还需要设定我们所需的ROP码,对于淡入操作,要求将源位图与模式画刷的反依次相与。对于淡出操作,要求将当前显示区域的位图与模式画刷依次相与。对于渐隐,我们需要把原位图与模式画刷相与后,把这个结果和当前显示区域的位图和画刷的镜象(原画刷的非)相与的结果相或。依次改变画刷,就可以得到渐隐的效果。这些操作的ROP码,MFC中并没有对应的预定义宏,但我们可以通过计算得到它,在Visual C++ 5.0 的在线文档“Ternary Raster Operations”中,详细介绍了计算方法。最终我们得到淡入、淡出操作的ROP码分别为000C0324、0x00A000C9。渐隐操作的ROP码是0x00AC0744。为了形成完整的动画效果,我们需要设置一个定时器来自动的执行这一系列的操作。
    下面用一个简单的例子说明模式画刷法的实现:
    1:建立一个基于对话框的项目,命名为PatternDemo.
    2:删除对话框上的“Todo:...”注释,并增加一个按纽,命名为“DEMO”
    3:为DEMO按纽加入对应的事件句柄OnDEMO(...).
    4:在CPatternDemo中加入私有成员变量如下:
    CDC *pdc;
    CDC memDC;
    CBitmap bmp;
    CBrush brush[8];
    UINT counter;
    UINT mode;
    UINT onrun;
    5:用VC自带的位图编辑器,按上文要求编辑8个8*8像素的单色位图,命名为  IDB_PATTERN1...IDB_PATTERN8。
    6:Import两个100*100像素的真彩bmp图片,命名为IDB_BMPSOURCE1和  IDB_BMPSOURCE2。
    7:使用ClassWizard为CPatternDemoDlg加入WM_CREATE的消息响应函数OnCreate(...),并在其中添加如下代码:
...

for(int i=0;i<8;i++)
    {
    bmp.LoadBitmap(IDB_PATTERN1+i);
    brush[i].CreatePatternBrush(&bmp);
    bmp.DeleteObject();
    }
...
    8:在CPatternDemoDlg::OnDEMO(...)函数中添加如下代码:
...
    if(!onrun)
   {
    pdc=GetDC();
    pdc->SetBkColor(RGB(0,0,0));
    pdc->SetTextColor(RGB(255,255,255));
    pdc->FillSolidRect(0,0,100,100,RGB(0,0,0));
    memDC.CreateCompatibleDC(pdc);
    bmp.LoadBitmap(IDB_BMPSOURCE1);
    memDC.SelectObject(&bmp);
    bmp.DeleteObject();
    mode=1;
    counter=0;
    SetTimer(1,200,NULL);
    onrun=1;
    }
...
    9:使用ClassWizard为CPatternDemoDlg加入WM_TIMER的消息响应函数OnTimer(...),并在其中 添加如下代码:

  1. if(mode==1) 
  2.     { 
  3.     if(counter>7) 
  4.     { 
  5.     mode=2; 
  6.     counter=0; 
  7.     return
  8.     } 
  9.     pdc->SelectObject(&brush[counter]); 
  10.     pdc->BitBlt(0,0,100,100,&memDC,0,0,0x000C0324); 
  11.     counter++; 
  12.     } 
  13.     if(mode==2) 
  14.     { 
  15.     if(counter>7) 
  16.     { 
  17.     mode=3; 
  18.     counter=0; 
  19.     return
  20.     } 
  21.     if(counter==0) 
  22.     { 
  23.     bmp.LoadBitmap(IDB_BMPSOURCE2); 
  24.     memDC.SelectObject(&bmp); 
  25.     bmp.DeleteObject(); 
  26.     } 
  27.     pdc->SelectObject(&brush[counter]); 
  28.     pdc->BitBlt(0,0,100,100,&memDC,0,0,0x00AC0744); 
  29.     counter++; 
  30.     } 
  31.     if(mode==3) 
  32.     { 
  33.     if(counter>7) 
  34.     { 
  35.     memDC.DeleteDC(); 
  36.     bmp.DeleteObject(); 
  37.     KillTimer(1); 
  38.     onrun=0; 
  39.     return
  40.     } 
  41.     pdc->SelectObject(&brush[counter]); 
  42.     pdc->BitBlt(0,0,100,100,NULL,0,0,0x00A000C9); 
  43.     counter++; 
  44.     } 


    9:在CPatternDemoDlg::CpatternDemoDlg()中加入:
...
onrun=0;
...
    10: 使用ClassWizard为CPatternDemoDlg加入WM_DESTORY的消息响应函数OnDestory(...),并在其中添加如下代码:
...
memDC.DeleteDC();
KillTimer(1);
...
编译运行该项目,可以看到第一幅图象从背景中渐渐的浮现出来,随后,又渐渐地隐入第二幅图象之中,接着,第二幅图象又慢慢地消失于背景中。

动画法:
    这种方法是利用直接操作位图的数据来实现的,可以实现像素颜色的平滑变化,视觉效果可以做的很好,因此,这种方法在屏保中的应用非常多。
    首先,我们必须了解bmp图形的结构。一个bmp图形由两个部分组成,即文件头和数据区,文件头存放bmp图形的大小、格式等信息,数据区存放bmp图形各个像素的颜色信息。对于24位真彩色的bmp来说,文件头的大小为54个字节,前14个字节对应VC中定义的BITMAPFILEINFO结构,后40个字节对应BITMAPINFOHEADER结构。我们把bmp数据区的数据读出,经过一定的运算,再利用WIN32API::StretchDIBits(...)函数直接输出到显示DC上,就可以实现一些特技效果。
    下面让我们分步去实现一个全屏幕的演示程序:
    1:生成一个基于对话框的项目,命名为F1:
    2:删除F1Dlg.h、F1Dlg.h和F1.cpp中与其相关的所有代码。
    3:在项目中添加一个基类为generic Cwnd的新类,命名为CW.
    4:为CW类添加如下私有成员变量:
    UINT y_offset;
    UINT x_offset;
    UINT stage;
    BYTE* p3;
    BYTE * p2;
    BYTE * p1;
    BITMAPINFOHEADER header;
    HGLOBAL hlb1;
    HGLOBAL hlb2;
    HGLOBAL hlb3;
    UINT start;
    UINT counter;


并在W.h的顶部加入宏定义 #define BMP_SIZE 192000
    5:为CW添加Create(...)虚函数,WM_CREATE,WM_TIMER,WM_PAINT,WM_DESTORY,WM_LBUTTONDOWN的消息句柄,接受缺省的函数名称。
    6:删除CW::Create(...)中的原有代码,用以下代码替换:
LPCTSTR m_lpszCN;    
    m_lpszCN = AfxRegisterWndClass(CS_BYTEALIGNCLIENT,
            ::LoadCursor(AfxGetResourceHandle(), 
            MAKEINTRESOURCE(IDC_NULLCORSOR)));
    return CWnd::CreateEx(WS_EX_TOPMOST,m_lpszCN, lpszWindowName, dwStyle, rect,     pParentWnd, nID, pContext);


    7: 删除CF1App:: InitInstance()中#endif以后的所有代码,用以下代码代替:
    int cx=GetSystemMetrics(SM_CXSCREEN);
    int cy=GetSystemMetrics(SM_CYSCREEN);
    CRect rectDefault(0,0,cx,cy); 
    m_pMainWnd=new CW();
    m_pMainWnd->Create(NULL, _T("Hello World!"), WS_VISIBLE|WS_POPUP,     rectDefault,NULL,NULL); 
    return TRUE;


    8:在CW::OnCreate(...)函数中加入如下代码:
    counter=0;   
    int cx=GetSystemMetrics(SM_CXSCREEN);
    int cy=GetSystemMetrics(SM_CYSCREEN);
    x_offset=(cx-640)/2;
    y_offset=(cy-400)/2;


    9: 在CW::OnDestroy()函数中加入如下代码:
    KillTimer(1);
    GlobalFree(hlb1);
    GlobalFree(hlb2);
    GlobalFree(hlb3);


    10: 在CW:: OnLButtonDown(...)函数中加入如下代码:
    SendMessage(WM_CLOSE);

    11: 在CW:: OnLButtonDown(...)函数中加入如下代码:

  1. CPaintDC dc(this); 
  2. dc.FillSolidRect(0,0,800,600,RGB(0,0,0)); 
  3. dc.SetTextColor(RGB(200,0,0)); 
  4. if(!start) return
  5. CFile f1,f2; 
  6. f1.Open("bmp1.bmp",CFile::modeRead); 
  7. f2.Open("bmp2.bmp",CFile::modeRead); 
  8. f1.Seek(14,CFile::begin); 
  9. f1.Read(&header,40); 
  10. f2.Seek(54,CFile::begin); 
  11. hlb1=GlobalAlloc(GMEM_MOVEABLE,BMP_SIZE); 
  12. p1=(BYTE*)GlobalLock(hlb1); 
  13. p1=(BYTE*)malloc(BMP_SIZE); 
  14. f1.ReadHuge(p1,BMP_SIZE); 
  15. GlobalUnlock(hlb1); 
  16. hlb2=GlobalAlloc(GMEM_MOVEABLE,BMP_SIZE); 
  17. p2=(BYTE*)GlobalLock(hlb2); 
  18. f2.ReadHuge(p2,BMP_SIZE); 
  19. GlobalUnlock(hlb2); 
  20. hlb3=GlobalAlloc(GMEM_MOVEABLE,BMP_SIZE); 
  21. p3=(BYTE*)GlobalLock(hlb3); 
  22. GlobalUnlock(hlb3); 
  23. f1.Close(); 
  24. f2.Close(); 
  25. stage=1; 
  26. SetTimer(1,100,NULL);  
  27. start=0; 


    12: 在CW:: OnTimer(...)函数中加入如下代码:

  1. if(stage==1) 
  2. if(counter++>63) 
  3.     stage=2; 
  4.     counter=0; 
  5.     return
  6. for(int i=0;i<BMP_SIZE;i++) 
  7.     p3[i]=counter*p1[i]/64; 
  8.     ::StretchDIBits(GetDC()->m_hDC,x_offset,y_offset,640,400,0,0,320,200,p3, 
  9.         ((BITMAPINFO*)(&header)),NULL,SRCCOPY); 
  10. if(stage==2) 
  11.     if(counter++>63) 
  12.     { 
  13.         stage=3; 
  14.         counter=0; 
  15.         return
  16.     } 
  17. for(int i=0;i<BMP_SIZE;i++) 
  18.     p3[i]=(64-counter)*p1[i]/64+counter*p2[i]/64; 
  19.     ::StretchDIBits(GetDC()->m_hDC,x_offset,y_offset,640,400,0,0,320,200,p3, 
  20.         ((BITMAPINFO*)(&header)),NULL,SRCCOPY); 
  21. if(stage==3) 
  22.     if(counter++>63) 
  23.     { 
  24.         KillTimer(1); 
  25.         SendMessage(WM_CLOSE); 
  26.         return
  27.     } 
  28. for(int i=0;i<BMP_SIZE;i++) 
  29.     p3[i]=(64-counter)*p2[i]/64; 
  30.     ::StretchDIBits(GetDC()->m_hDC,x_offset,y_offset,640,400,0,0,320,200,p3, 
  31.         ((BITMAPINFO*)(&header)),NULL,SRCCOPY); 


    13:最后,将两个分辨率为320*200的24bitBMP拷入工程所在的目录中,分别命名为1.bmp和2.bmp. 编译运行程序,可以看到在黑色的背景中,第一幅图象(1.bmp)由暗渐渐变亮,当它完全出现后,第二幅图象(2.bmp)从第一幅图象中慢慢的浮现出来。当第二幅图象完全取代第一幅后,它的亮度又逐渐减小,最终消失在黑色的背景中。与模式画刷法不同,这些过渡非常平滑。

(wiseman)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201005/9545.html]
本文出处:博客园 作者:wiseman
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容