1. 程式人生 > >在ARM上的快速AlphaBlend演算法解析。透明框體實現。

在ARM上的快速AlphaBlend演算法解析。透明框體實現。

近來小弟研究AlphaBlend演算法,即透明框在TQ2440+4.3寸屏上的顯示透明框問題。幾經週轉  總結如下:

混合演算法目前在常用到的演算法是AlphaBlend。所謂Alpha-Blending,其實就是按照“Alpha”混合向量的值來混合源畫素和目標畫素。alpha-blending技術:渲染包含alpha通道值的圖形即稱為alpha-blending技術。渲染的意思是把包含alpha通道值的象素畫在目標裝置上。而目標裝置本來就有背景色或者背景圖片,目標裝置可以是真正的顯示屏,也可以是記憶體中的一塊邏輯屏。

計算公式如下:假設一幅圖象是A,另一幅透明的圖象是B,那麼透過B去看A,看上去的圖象C就是B和A的混合圖象,設B圖象的透明度為alpha(取值為0-1,1為完全透明,0為完全不透明). Alpha混合公式如下:

                 R(C)=(1-alpha)*R(B) +alpha*R(A)

                  G(C)=(1-alpha)*G(B) +alpha*G(A)

                  B(C)=(1-alpha)*B(B) +alpha*B(A)

R(x)、G(x)、B(x)分別指顏色x的RGB分量原色值。從上面的公式可以知道,Alpha其實是一個決定混合透明度的數值。改變這個 alpha 值可以得到一個漸變的效果。分離RGB色用"位與"、"移位"運算的方法;透明的比例按2的N次冪來分級,這樣可以快速運算。如果是按32級Alpha = 1/32,B圖的權重 = (32-n)/32,則A圖的權重=n/32,可以得到這樣的演算法:

R(C)=(32-n) * R(B)+ n* R(A);

再對R(C)右移5位(除以32)就可以了

具體演算法如下:1)先把源畫素和目標畫素的 RGB 三個顏色分量分離。2)然後把源畫素的三個顏色分量分別乘上 Alpha 的值,並把目標畫素的三個顏色分量分別乘上 Alpha 的反值。3)把結果按對應顏色分量相加。4)對最後求得的每個分量結果除以 Alpha 的最大值(alpha值,16位色的色深為32級,故alpha取值0-32,故右移5位,編譯器處理位移比除法快的多)。5)最後把三個顏色分量重新合成為一個畫素輸出。

在程式上,首先的讀會某一畫素點的值,這點可以借鑑天嵌的列印畫素點函式,反向讀取某一點對應的畫素即可,程式修改為讀取值c =LCD_BUFFER[(y)][(x)];讀取後就將對就是對資料的分離了,用公式src2=((src<<16)|src)&0x07E0F81F;對目標畫素進行轉換成32為畫素,並且畫素格式由R:G:B改為了G:R:B格式,同樣對源畫素進行處理,把源影象乘以Alpha正直,目標影象乘以反值,將倆影象分量相加,dst2= src2*alpha+ (1- alpha)*dst2,alpha最大取值32則:dst2= src2*alpha+(32-alpha)*dst2/32,變形可得:dst2=(src2-dst2)*alpha/32+dst2,將處法變為位移得:dst2=((src2-dst2)*alpha)>>5+dst2,對得到結果去掉溢位位可得:dst2=((((src2-dst2)*alpha)>>5)+dst2)&0x07E0F81F;最後用(dst2>>16)|dst2;變回16位資料格式,則該dst2即為實現了透明框的畫素點。依次轉換需要實現的透明框的畫素點即可實現透明框。從上面的流程可以看出,alpha值越大,透明效果就越弱。當alpha值達到最大時,就是不透明的,相反,如果alpha值為0。則是全透明。


快速ALPHA BLENDIN演算法

//src:源顏色 //dst:目標顏色 //alpha:透明程度(0~32) //返回值:混合後的顏色.

u16 gui_alpha_blend565(u16src,u16 dst,u8 alpha)

{

    u32 src2;

    u32 dst2;  

    src2=((src<<16)|src)&0x07E0F81F;

    dst2=((dst<<16)|dst)&0x07E0F81F;   

    //dst2=((((dst2-src2)*alpha)>>5)+src2)&0x07E0F81F;

    dst2=((((src2-dst2)*alpha)>>5)+ dst2)&0x07E0F81F

    return (dst2>>16)|dst2;  

int GetPixel(U32 x, U32 y)  //讀取原影象素點 由上面的函式改編過來

{

    U32 c;

    if((x < SCR_XSIZE_TFT) && (y < SCR_YSIZE_TFT))

        c = LCD_BUFFER[(y)][(x)];

    return c;

}

//x,y,width,height:區域設定//color:alphablend的顏色//aval:透明度(0~32)

void gui_alphablend_area(U16x,U16 y,U16 width,U16 height,U16 color,U8 aval)

{

    U16 i,j;

    U16 tempcolor;

    for(i=0;i<width;i++)

    {

        for(j=0;j<height;j++)

        {

            tempcolor=GetPixel(x+i, y+j);

            tempcolor=gui_alpha_blend565(tempcolor,color,aval);

            PutPixel(x+i,y+j,tempcolor);

        }

    }

}


在這裡透過B去看A,或者透過A去看B,其實無非不過區別就是alpha的正值和alpha反值區別。程式的體現就在於

dst2=((((dst2-src2)*alpha)>>5)+src2)&0x07E0F81F;

    dst2=((((src2-dst2)*alpha)>>5)+ dst2)&0x07E0F81F;

最終效果都一樣能實現透明框演算法。無非不過 透明度取值問題。

64K 色模式下的快速 Alpha混合演算法

在 32/64k 色模式下,由於每個點的 RGB 值是放在一個字裡,以 16bit 色為例, 一般是按 RGB 或 BGR 565 存放. 傳統的軟體 Alpha 混合演算法是先將 RGB 分離出來, 分開運算,然後再合成. 這造成了 16bit 模式下的 alpha 混合比 24bit 模式下慢 的現象,但使用 16bitcolor 真的那麼慢嗎? 我認為如果不使用 MMX 指令, 15/16 的比 24bit 的快. 因為我們可以使用一個小的技巧來同時計算 RGB. 而 24 bit 顏色,除非使用 MMX 指令,否則必須分開計算 R G B.

先設 color 是 RGB 565 的, 那麼按 2 進位制看, 這個顏色字是這樣分佈的:

RRRRR     GGGGGG   BBBBB

5位                    6位           5位

而 386 以上 CPU 都有 32 位的暫存器,我們只需要將 16bit RGB 變形為

00000       GGGGGG 00000       RRRRR     000000     BBBBB

5位                    6位 5位        5位           6位           5位

儲存在 32 位暫存器中,(就是把綠色提到前 16 位裡) 由於64k 色下顏色深度是 32 級的,所以 alpha 也只用分 32 級就能滿足需要. 那麼對上面變形過的雙字處理,可以同時算 RGB 了. (Color1*Alpha+Color2*(32-Alpha))/32 能不能簡化為(Color1-Color2)*Alpha/32+Color2.? 我思考過這個問題,以為減法將產生負數,這樣再算乘法時有可能出問題,但是經過測試,這樣簡化似乎又沒有問題. 畢竟極小的誤差是可以忽略的

來源一:http://blog.csdn.net/xhhjin/article/details/6445460

alpha混合其實很簡單,要做的只是分解源顏色c1,目的顏色c2,然後將顏色分量r1,g1,b1和r2,g2,b2分別按照公式(clr*alpha+clr*(32-alpha))/32來計算,最後再組合成一個顏色值即可,可是如此計算,運算量很大速度很慢。 所以現在就要用到一個技巧,首先,就16位色來說一般的格式是565,表示rgb分量的二進位制位數,如圖1。 那麼我們就可以利用一個32位的變數來把這個顏色的綠色分量提前,變為如圖2的格式。這樣每個顏色分量中間就有了進位的空間,也就不用分解這個顏色值了。然後,將變形完的兩個顏色值按照上面公式進行計算,計算完畢再變回565的格式就完成了一次alpha混合的計算。 

c語言原始碼如下:

__inline void MakeAlpha(WORD* wpSrc, WORD* wpDes, WORD wAlpha)
{
   register DWORD d1; // 計算用的中間變數,宣告為暫存器變數快些
   register WORD wa = *wpSrc; // 源顏色
   register WORD wb = *wpDes; // 目的顏色   register DWORD alpha = wAlpha; // alpha值,16位色的色深為32級,故alpha取值0-32
// (c1-c2)*alpha/32+c2
(c1*alpha+c2*(32-alpha))/32變形

// 而來,減少了一次乘法運算
//
下面的式子故意寫成這樣,編譯器會把它處理的很好
//
要比這樣快一些
// c1 = (((wa << 16) | wa) & 0x7e0f81f);

// 16位變形32 0x7e...f為二進位制的00000111111000001111100000011111
// c2 = (((wb << 16) | wb) & 0x7e0f81f); 
// d1 = ((c1-c2)*alpha)>>5+c2;

// 除以32等於右移5位,但是位移操作要比乘除法快的多,

// 例如:a*320可以寫成a*256+a*64=>(a<<8)+(a<<6)

   d1 = (((((((wa << 16) | wa) & 0x7e0f81f) - (((wb << 16) | wb) & 0x7e0f81f)) * alpha) >> 5) + (((wb << 16) | wb) &0x7e0f81f)) & 0x7e0f81f;
        wa = (d1 & 0xffff0000)>>16; // g...r...b => ..g..
        wb = d1 & 0xffff; // g...r...b => r...b
        *wpDes = wa | wb; // rgb
}

來源二:http://blog.chinaunix.net/uid-8272118-id-2033330.html

w_201505於百葉路