现在的位置: 首页 > 综合 > 正文

嵌入式下高效率半透明的实现

2013年12月04日 ⁄ 综合 ⁄ 共 6188字 ⁄ 字号 评论关闭

原文地址::http://blog.csdn.net/ricky460/article/details/234229

在嵌入式系统下 , 由于嵌入式系统本身资源内存的局限性 ,导致在Windows下能够轻松实现的功能和效果在嵌入式系统下实现起来却变得非常困难 。而且 ,WinCE下 , 由于微软为了保证WinCE这个OS操作系统能够足够小 , 所以减少很多Windows的组件 ,只保留了OS很核心的一部分 。在这种情况下 ,WinCE操作系统能够缩减到足够小  。但是 ,微软的这种做法却给我们编程人员带来极大的不便 。 导致原先我们Windows下信手拈来 ,随叫随到的API 变的如此陌生 ,我们不得不另僻曲径,寻找一种替代的方法来实现相同的功能 。同时 ,这也要求我们编程人员对原先不太关心的原理知识做更细致的了解 。

    
     
就拿半透明来说 , 嵌入式下的半透明是困扰很多嵌入式编程人员的难题 ,就笔者来说 ,我寻找了大量国内,国外的网站 ,也咨询过几十位有多年嵌入式方面工作经验的人员 ,但是没有一个人能够给我良好的解决方案 。 目前的实现方法大多数直接写硬件或者直接写显存这样的底层方法来实现。是后来 ,在笔者自己的努力下 ,比较完美的在应用层上解决了问题 ,实现高效率的半透明效果。

     我们先谈谈window下常规半透明的实现方法 。

常规半透明的实现方法是 :
 

1: 首先获取目标DC某位置像素点的颜色值 ,将此颜色值拆分为R ,G ,B值(假设为R1 , G1 , B1) ,

2: 再获得源DC此位置像素点的颜色值 ,也将此颜色值拆分为R ,G ,B值(假设为R2 , G2 , B2) ,

3: 然后将此两颜色的R  , G ,B值分别按如下公式进行运算 。

                             
                   New  = Source * Alpha + ( 1 – Alpha) * Dest ;

    分别计算:
   
                   NewR = R1 * Alpha + (1 – Alpha) * R2 ;

                   NewG = G1 * Alpha + (1 – Alpha) * G2 ;

                   NewB = B1 * Alpha + (1 – Alpha) * B2 ;

4:最后将计算得到的新NewR,NewG,NewB值合成最终的将显示出来的颜色值 。

 

     这里需要对半透明区域内所有的像素点都进行如上4步操作 ,这将涉及到大量的运算 。但这并不是最耗时的 。 最耗时的是获得像素点的颜色值以及设置某个像素点的像素值 。即GetPixel()和SetPixel() 。

在WinCE4.2模拟器下做测试 ,如下一段代码

for(int i = 0 ; i < 100 ; i ++ )

{

for(int n = 0 ; n < 100 ; n ++)

{

pDC->GetPixel(i ,n );

}

}

需要运行4秒钟 ,即10000次GetPixel操作需要4秒钟 ,而这只是一块宽100 ,长100的半透明区域而已 。

    所以  最终我们需要用某种替代的方法来代替这个GetPixelSetPixel函数 。

    在Windows下 , 可以用GetDIBitsGetBitmapBits来一次性的将所有的像素点读取过来进行分析。

但是在嵌入式下 , 很不幸 , 该死的微软把这两个函数给Del掉了 ,所以 , 这又必须要求我们寻找另一种替代的方法来实现 。 在细细的研究了BMP的结构 , 和DIB设备无关图以后 ,找到了一种方法来实现半透明的功能 ,并且在嵌入式系统能够有很高的绘制效率 。

 

操作系统: WinCE 4.2 , Emulator  , x86 模拟器 。

编程环境: Embedded Visual  C ++  , SP4  , StandardSDK

/*-------------------------------------------------------------
  2004-12-1  Ricky 黄俊峰 编写  
  Function:   在显示DC上画一块半透明区域
  In:

  CDC              *pDstDC      目标DC
  CRect              rtTran          在目标DC上的半透明区域
  COLORREF   clrSrc          与目标区域进行Alpha混和的颜色
 
 需要优化的地方
 1:  传入透明度做为参数
        这里半透明运算 ,没有用常规的拆分法来做 , 为了加快速度 , 将浮点运算改成了移位运算
        为了方便 ,这里统一显示的是50%透明度的半透明效果
       实际想改成任意透明度的话 ,也是非常方便的。详细修改方法 ,请查看我的另一篇关于Alpha
       混和的文章。
2: 用RGBQUAD* pRgb 代替 BYTE *pbtPixel ; 一次进行多个数值的运算 。
3: 嵌入MMX汇编指令 ,加快运算速度 。

     ------------------------­--­--­--­--­------------------------------*/
void DrawSemiTransparentRect(CDC *pDstDC, CRect rtTran, COLORREF clrSrc)
{
        int nSavedDC = pDstDC->SaveDC();                                        //保存原始DC

        int nWidth   = rtTran.Width();                                                     //半透明区域的宽度
        int nHeight  = rtTran.Height();                                                     //半透明区域的高度

        
        //拷屏 , 新建一个DC来拷贝目标区域
        CDC  bmpDC;                                                             
        bmpDC.CreateCompatibleDC(pDstDC);
        CBitmap  bmp ;
        bmp.CreateCompatibleBitmap(pDstDC , nWidth , nHeight);
        CBitmap* pOldBmp = (CBitmap*)bmpDC.SelectObject(&bmp);
        bmpDC.BitBlt(0 , 0 , nWidth , nHeight , pDstDC , rtTran.left , rtTran.top , SRCCOPY);

        //获取bmpDC中的图形数据到BYTE数组
        HDC hDIBDC = CreateCompatibleDC(NULL);

        BITMAPINFO hdr; 
        ZeroMemory(&hdr , sizeof(BITMAPINFO));

        hdr.bmiHeader.biSize         =  sizeof(BITMAPINFOHEADER);
        hdr.bmiHeader.biWidth      =  nWidth;
        hdr.bmiHeader.biHeight     =  nHeight;
        hdr.bmiHeader.biPlanes     =  1;
        hdr.bmiHeader.biBitCount =  32;        /* 这里统一用32位图形 ,原因是32位图形结构相对固定 , 每个
                                                                            像素用4个字节表示 。每行末尾不需要添加额外的字节。
                                                                      而24位图形 , 每行末尾需要添加额外的字节 , 以保证每行的
                                                                            字节数是4的整数倍
                                                                      如果想缩减开销和字节数 , 可以采用24位的图形 ,不过就会多
                                                                               出一些判断添加额外自己的代码 。*/

        BYTE * pbtPixels = NULL ; 

        HBITMAP hDIBitmap  =  CreateDIBSection(hDIBDC, 
                                                                     (BITMAPINFO *)&hdr, 
                                                                     DIB_RGB_COLORS,
                                                                     (void **)&pbtPixels, 
                                                                     NULL, 
                                                                     0);

       HBITMAP hOldBmp = (HBITMAP)SelectObject(hDIBDC, hDIBitmap);
       BitBlt(hDIBDC,0,0,nWidth,nHeight,bmpDC,0,0,SRCCOPY);
       SelectObject(hDIBDC, hOldBmp);

      
      //对BYTE数组进行Alpha混和
       int nPixelSize = 4 ;                                                    //每个像素点用4个字节来存储

       BYTE btSR = GetRValue(clrSrc);
       BYTE btSG = GetGValue(clrSrc);
       BYTE btSB = GetBValue(clrSrc);

       for(int i = 0 ; i < nHeight ; i ++)
      {
             for(int j = 0 ;  j < nWidth ; j ++)
            {
   
                       BYTE btB = pbtPixels[i *  nWidth * nPixelSize   + j * nPixelSize  ] ;
                       BYTE btG = pbtPixels[i *  nWidth * nPixelSize   + j * nPixelSize + 1 ] ;
                       BYTE btR = pbtPixels[i *  nWidth * nPixelSize   + j * nPixelSize + 2 ] ;

                       btB = (btSB + btB ) >> 1 ; //btB = btSB * 0.5 + (1-0.5) * btB;
                       btG = (btSG + btG ) >> 1 ; //btG = btSG * 0.5 + (1-0.5) * btG;
                       btR = (btSR + btR ) >> 1 ; //btR = btSR * 0.5 + (1-0.5) * btR;
   

                       pbtPixels[i *  nWidth * nPixelSize   + j * nPixelSize  ]     = btB ;  
                       pbtPixels[i *  nWidth * nPixelSize   + j * nPixelSize + 1 ] = btG ; 
                       pbtPixels[i *  nWidth * nPixelSize   + j * nPixelSize + 2 ] = btR ;

             }
        }

 

        //绘制最终半透明图形到目标区域
 
        SetDIBitsToDevice(pDstDC->GetSafeHdc(),
                                       rtTran.left,
                                       rtTran.top,
                                       nWidth,
                                       nHeight,
                                       0,
                                       0,
                                       0,
                                       nHeight,
                                       (void*)pbtPixels,
                                       (BITMAPINFO*)&hdr,
                                       DIB_RGB_COLORS);

           //析构和清空
           bmpDC.SelectObject(pOldBmp);
           bmp.Detach();
           delete [] pbtPixels ;
           DeleteObject(hDIBDC);
           pDstDC->RestoreDC(nSavedDC);                                                         //恢复初始DC
}

抱歉!评论已关闭.