基本原理 在Visual C++6.0中,显示位图的方法及过程如下: 1. 显示程序资源中的位图(位图的所有数据均存在于可执行文件中) (1)从资源中装入位图 ● 定义位图对象数据成员CBitmap m_Bitmap; ● 调用CBitmap成员函数LoadBitmap(),如m_Bitmap.LoadBitmap(IDB_BITMAP1); ● 传入LoadBitmap的参数是位图在图形编辑器中生成或从位图文件中引入时赋予的识别符。 (2)生成与位图相联系的内存设备情境对象 CDC MemDC; MemDC.CreateCompatibleDC(NULL); MemDC.SelectObject(&m_Bitmap); (3)显示位图 CClientDC ClientDC(this); BITMAP BM; m_Bitmap.GetObject(sizeof(BM),&BM); ClientDC.BitBlt ( X,Y, //目标设备逻辑横、纵坐标 BM.bmWidth, BM.bmHeight, //显示位图的像素宽、高度 &MemDC, //待显示位图数据的设备情境对象 0,0, //源数据中的横、纵坐标 SRCCOPY); //位操作方式 这种方法显示位图速度快,但不是很灵活,而且会使可执行文件增大。 2. 显示独立文件方式的位图(位图的所有数据独立于可执行文件) HBITMAP *hBitmap; //定义位图对象句柄 BITMAP BM; CDC MemDC; CClientDC ClientDC(this); MemDC.CreateCompatibleDC(&ClientDC); hBitmap=(HBITMAP*):: LoadImage ( AfxGetInstanceHandle(), //取得应用程序句柄 “demo1.bmp”, //位图文件名 IMAGE_BITMAP, //类型为Windows位图 0,0, LR_LOADFROMFILE); //从文件中取位图数据 MemDC.SelectObject(hBitmap); :: GetObject(hBitmap,sizeof(BM),&BM); ClientDC.BitBlt(……) //使用格式与方法一同 这种方法显示位图速度较之前一种慢了一点,但其灵活性较大,可以任意变换位图文件,而无需重新编译源程序, 也减小了可执行文件的大小。 实现方法 下面介绍各种图形显示技巧的具体实现原理及方法。以下所有程序算法的实现均可放在视类(CView,也可视自己的需要放在其他类)中处理,且有必要进行如下的相关操作: 增加如下类成员变量: BITMAP m_Bm; //保存位图的宽、高度等数据 HBITMAP *m_hBitmap; //保存位图数据句柄 CDC m_MemDC; //内存设备情境对象 在类构造函数中加入如下代码: m_MemDC.CreateCompatibleDC(NULL); //产生内存设备情境对象 m_hBitmap=(HBITMAP *)::LoadImage( //从文件中装入位图数据 AfxGetInstanceHandle(), “demo1.bmp”, IMAGE_BITMAP, 0,0, LR_LOADFROMFILE ); m_MemDC.SelectObject(m_hBitmap); //将位图选入内存设备情境对象 ::GetObject(m_hBitmap,sizeof(m_Bm),&m_Bm); 1. 水平交错效果 原理:将内存设备情境对象(如MemDC)中的位图数据拆分成奇、偶扫描线两部分,其中奇数条扫描线由上往下移动,偶数条扫描线则由下往上移动,且两者同时进行。屏幕上的效果为分别由上下两端出现的较淡栅栏图形,逐渐相互靠近,直至整个位图完全清楚。垂直交错效果的实现原理与之类似。 程序算法: int i,j; for ( i=0; i<=m_Bm.bmHeight; i+=2 ) {j = i; while ( j>0 ) {ClientDC.StretchBlt( //奇数,由上至下 0,j-1, //目标设备逻辑横、纵坐标 m_Bm.bmWidth,1, //显示位图的像素宽、高度 &m_MemDC, //源位图设备情境对象 0,m_Bm.bmHeight-(i-j-1), //源位图的起始横、纵坐标 m_Bm.bmWidth,1, //源位图的像素宽、高度 SRCCOPY); ClientDC.StretchBlt( //偶数,由下至上 0,m_Bm.bmHeight-j, //目标设备逻辑横、纵坐标 m_Bm.bmWidth,1, //显示位图的像素宽、高度 &m_MemDC, //源位图设备情境对象 0,i-j, //源位图的起始横、纵坐标 m_Bm.bmWidth,1, //源位图的像素宽、高度 SRCCOPY); j-=2; } // while ( j>0 ) Sleep(10); } //for ( i=0; i<=m_Bm.bmHeight; i+ =2 ) 2. 雨滴效果 原理:将内存设备情境对象(如MemDC)中位图数据的最后一条扫描线,顺序地从目标设备(如ClientDC)中待显示位图的第一条扫描线所在位置移动至最后一条处,并保留此条扫描线在屏幕上移动时留下的轨迹。接着再把MemDC中位图数据的倒数第二条扫描线,顺序地从目标设备(如ClientDC)中待显示位图的第一条扫描线所在位置移动至倒数第二条处。其余的扫描线依此类推。 程序算法: int i,j; for ( i=0; i<=m_Bm.bmHeight; i++ ) {for ( j=0; j<=m_Bm.bmHeight-i; j++ ) ClientDC.StretchBlt( 0,j, //目标设备逻辑横、纵坐标 m_Bm.bmWidth,1, //显示位图的像素宽、高度 &m_MemDC, //源位图设备情境对象 0,m_Bm.bmHeight-i, //源位图的起始横、纵坐标 m_Bm.bmWidth,1, //源位图的像素宽、高度 SRCCOPY); Sleep(20); } //for ( i=0; i<=m_Bm.bmHeight; i++ ) 3. 百叶窗效果 原理:将内存设备情境对象(如MemDC)中的位图数据分成若干组,然后分别从第一组到最后一组进行搬移,第一次搬移每组中第一条扫描线到目标设备(如ClientDC)中待显示位图的相应位置,第二次搬移每组中第二条扫描线,接着第三条、第四条扫描线。 程序算法: int i,stepi,j; stepi=m_Bm.bmHeight/10; for ( i=0; i<=stepi; i++ ) {for ( j=0; j<10; j++ ) ClientDC.StretchBlt( 0,j*stepi+i, //目标设备逻辑横、纵坐标 m_Bm.bmWidth,1, //显示位图的像素宽、高度 &m_MemDC, //源位图设备情境对象 0,j*stepi+i, //源位图的起始横、纵坐标 m_Bm.bmWidth,1, //源位图的像素宽、高度 SRCCOPY); Sleep(20); } //for ( i=0; i<=stepi; i++ ) 4. 随机积木效果 原理:将内存设备情境对象(如MemDC)中的位图数据分成纵横十等份共一百组数据,然后随机地取出这一百组数据中的某一组显示到目标设备(如ClientDC)中待显示位图的相应位置,如此反复直到所有一百组数据均显示完毕为止。 程序算法: int i,j,stepx,stepy,dispnum,x,y; int pxy[10][10]; //使用本数组记录已显示过的数据组 for ( i=0; i<10; i++ ) for ( j=0; j<10; j++ ) pxy[i][j]=0; stepx=m_Bm.bmWidth/10; stepy=m_Bm.bmHeight/10; srand( (unsigned)time( NULL ) ); dispnum=0; //记录已显示过的数据组的个数 while(1) { x=rand() % 10; y=rand() % 10; if ( pxy[x][y] ) //本组x,y所代表的数据组是否已显示过? continue; pxy[x][y]=1; //表明本组x,y所代表的数据组已显示过 ClientDC.StretchBlt( x*stepx, y*stepy, //目标设备逻辑横、纵坐标 stepx,stepy, //显示位图的像素宽、高度 &m_MemDC, //源位图设备情境对象 x*stepx, y*stepy, //源位图的起始横、纵坐标 stepx,stepy, //源位图的像素宽、高度 SRCCOPY); dispnum++; if ( dispnum >=100 ) break; Sleep(30); } // while(1) 结 语 以上程序代码均在Visual C++ 6.0中调试通过,所有片断均可编写成独立的函数,灵活使用。如果对以上几种显示效果进行变换,我们还可以实现多种其他特技效果。 

解决方案 »

  1.   


      经常看电视的朋友们不知注意到没有,最近的电视连续剧在每集片头或片尾部分都有显示一些特殊效果的图像,比如前一阵子中央一套放的《长征》、目前中央八套正在播放的《康熙王朝》,这些特效称为"图像的浮雕效果"和"图像的雕刻效果",经过这些特效处理后的图像增强了观众们的视觉效果,它们看上去仿佛是使用3D技术作的,这也是为什么这种技术那么流行的原因吧。其实,我们完全可以用一些简单的数字图像处理算法来实现这些看似复杂高深的显示效果。我们手头上的一些关于利用VC开发数字图像资料大都是讲解如何控制图像的每一行或每一列像素的显示时间或顺序来实现一些简单的图像显示效果,涉及到图像算法的文章很少,本文针对上述的这两种图像特效处理技术并加以引伸,讲解了一些实现图像特效算法,以一个标准的Lena灰度图像为原图,给出了处理后的效果图,同时给出了VC开发平台上的部分实现源代码。  1."浮雕"图像  "浮雕"图象效果是指图像的前景前向凸出背景。所谓的"浮雕"概念是指标绘图像上的一个像素和它左上方的那个像素之间差值的一种处理过程,为了使图像保持一定的亮度并呈现灰色,我在处理过程中为这个差值加了一个数值为128的常量。需要读者注意的是,当设置一个像素值的时候,它和它左上方的像素都要被用到,为了避免用到已经设置过的像素,应该从图像的右下方的像素开始处理,下面是实现的源代码:void CDibView::OnFDImage() //产生"浮雕"效果图函数
    {
     HANDLE data1handle;
     LPBITMAPINFOHEADER lpBi;
     CDibDoc *pDoc=GetDocument();
     HDIB hdib;
     unsigned char *hData;
     unsigned char *data;
     hdib=pDoc->GetHDIB();
     //我是如何打开图像文件并得到图像数据,请感兴趣的朋友参考
     //天极网上我的相关文章,那里有详细的论述,这里不再赘述。
     BeginWaitCursor();
     lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib);
     hData=(unsigned char*)FindDIBBits((LPSTR)lpBi);
     pDoc->SetModifiedFlag(TRUE);
     data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight);
     //声明一个缓冲区用来暂存处理后的图像数据
     data=(unsigned char*)GlobalLock((HGLOBAL)data1handle);
     AfxGetApp()->BeginWaitCursor();
     int i,j,buf;
     for( i=lpBi->biHeight; i>=2; i--)
      for( j=lpBi->biWidth; j>=2; j--)
      {
       //"浮雕"处理 
       buf=*(hData+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)-*(hData+(lpBi->biHeight-i+1)*WIDTHBYTES(lpBi->biWidth*8)+j-1)+128;
       if(buf>255) buf=255;
       if(buf<0)buf=0;
       *(data+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)=(BYTE)buf;
      }  for( j=0; jbiHeight; j++)
       for( i=0; ibiWidth; i++)
        //重新写回原始图像的数据缓冲区
        *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j);
        AfxGetApp()->EndWaitCursor();
        GlobalUnlock((HGLOBAL)hdib);
        GlobalUnlock(data1handle);
        EndWaitCursor();
        Invalidate(TRUE);//显示图像
      } 
      2."雕刻"图像  上面讲述了通过求一个像素和它左上方像素之间的差值并加上一个常数的方法生成"浮雕"效果的灰度图像,"雕刻"图像与之相反,它是通过取一个像素和它右下方的像素之间的差值并加上一个常数,这里我也取128,经过这样处理,就可以得到"雕刻"图像,这时候图像的前景凹陷进背景之中。同样需要读者注意的是为了避免重复使用处理过的图像像素,处理图像时要从图像的左上方的像素开始处理。实现代码如下:void CDibView::OnDKImage() 
    {
     // TODO: Add your command handler code here
     HANDLE data1handle;
     LPBITMAPINFOHEADER lpBi;
     CDibDoc *pDoc=GetDocument();
     HDIB hdib;
     unsigned char *hData;
     unsigned char *data;
     hdib=pDoc->GetHDIB();
     BeginWaitCursor();
     lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib);
     hData=(unsigned char*)FindDIBBits((LPSTR)lpBi);
     pDoc->SetModifiedFlag(TRUE);
     data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight);
     data=(unsigned char*)GlobalLock((HGLOBAL)data1handle);
     AfxGetApp()->BeginWaitCursor();
     int i,j,buf;
     //图像的"雕刻"处理
     for( i=0;i<=lpBi->biHeight-2; i++)
      for( j=0;j<=lpBi->biWidth-2; j++)
      {
       buf=*(hData+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)-*(hData+(lpBi->biHeight-i-1)*WIDTHBYTES(lpBi->biWidth*8)+j+1)+128;
       if(buf>255) buf=255;
       if(buf<0)buf=0;
       *(data+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)=(BYTE)buf;
      }
     for( j=0; jbiHeight; j++)
     for( i=0; ibiWidth; i++)
      //重新将处理后的图像数据写入原始的图像缓冲区内
      *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j);
      AfxGetApp()->EndWaitCursor();
      GlobalUnlock((HGLOBAL)hdib);
      GlobalUnlock(data1handle);
      EndWaitCursor();
      Invalidate(TRUE);
     }  
      

  2.   

    在http://www.comprg.com.cn上有关于这些的东西!
      

  3.   

    hujun614(胡均)
    真是牛人,ADMIRE
      

  4.   

    经常向内存导入bmp图片,次数多了好像会非法程序
    还是把文件里的图片导入比较好