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

post0發表於2007-08-12
64K色模式下的快速Alpha混合演算法(轉)[@more@]

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

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

  

  RRRRR GGGGGG BBBBB

  5位  6位   5位

  

  RGB成分

  

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

  

  00000 GGGGGG 00000 RRRRR 000000 BBBBB

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

  

  變形後的RGB成分

  

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

  

  最近溫習了一下彙編,今天用NASM寫了個C可呼叫的Alpha混合函式(32位模式,我針對DJGPP寫的)並進行了Pentium最佳化(針對雙流水線,有錯請指出)。大家看看,有BUG,還能最佳化或有更快的方法也請一定告訴我。順便提一下,上面提到的化簡沒有體現到下面的程式中,而且,使用乘法本身是個錯誤。只是看看吧,如果你想實際運用,請參考Allegro程式庫的做法。

  

  ; 對16位的color1與color2進行Alpha混合

  ; R=(r1*alpha+r2*(32-alpha))/32

  ; G=(g1*alpha+g2*(32-alpha))/32

  ; B=(b1*alpha+b2*(32-alpha))/32

  

  ; C 語言呼叫函式(32 位保護模式)Pentium雙流水線最佳化

  ; By Cloud Wu (cloudwu@263.net)

  ;       (~cloudwu)

  ; -------------------------------------------------------------------------

  ; unsigned long alpha (unsigned long c1,unsigned long c2,unsigned long alpha);

  ; -------------------------------------------------------------------------

  ; c1: 顏色1的RGB(565),c2: 顏色2的RGB(565),alpha: Alpha值(0~31)

  ; NASM 編譯透過

  

  [BITS 32]

  [GLOBAL _alpha]

  [SECTION .text]

  

  _alpha:

  

  ; 初始化程式碼

  push ebp      ; ebp 壓棧

  mov ebp,esp    ; 儲存 esp 到 ebp

  mov edi,0x7e0f81f ; dx=00000111111000001111100000011111

  add esp,8     ; esp 指向引數 c1

  pop eax      ; 彈出 c1 到 ax

  pop ebx      ; 彈出 c2 到 bx

  

  ; 處理顏色

  mov cx,ax     ; cx=r1..b1

  mov dx,bx     ; dx=r2..b2

  sal eax,16     ; eax=r1g1b1......

  sal ebx,16     ; ebx=r2g2b2......

  mov ax,cx     ; eax=r1g1b1r1g1b1

  mov bx,dx     ; ebx=r2g2b2r2g2b2

  and eax,edi    ; eax=..g1..r1..b1

  pop esi      ; 彈出 alpha

  mul esi      ; eax*=alpha

  neg esi      ; -alpha

  and ebx,edi    ; ebx=..g2..r2..b2

  add esi,0x20    ; 32-alpha

  xchg eax,ebx    ; 交換 eax,ebx

  mul esi      ; c2*=(32-alpha)

  add eax,ebx    ; c1*alpha+c2*(32-alpha)

  mov esp,ebp

  sar eax,5     ; color=(c1*alpha+c2*(32-alpha))/32

  

  ;還原成 RGB 形式

  pop ebp

  and eax,edi    ; color=..g..r..b

  mov cx,ax     ;

  sar eax,16     ;

  or ax,cx      ; color=rgb (eax)

  ret

  

  如果建一張256K的表來查表預處理RGB怎樣?經過嘗試,發現速度不僅沒有提高,反而降低了。分析的結論是,256K的表太大了,以至於不能放到快取(Cache)裡,反而沒有計算的方法快,畢竟計算的話,每行的程式碼都很快,而不必和記憶體打交道。真正加速的方法是什麼?借鑑Allegro程式庫裡的方法,建立32個函式分別計算每個alpha值的情況。這樣,alpha值變成固定的,從而可以使用LEA、ADD、SUB、SAL、SAR來替代緩慢的MUL。經過實踐,我重寫了Allegro程式庫裡的cblend15.c及cblend16.c,(使用程式庫自己的Test.exe,機器配置為Cyrix Gx/120 S3/375 4M)測試資料如下:

  

  原有的混合函式 使用新演算法的混合函式 將Blender函式置為空

  1402 per sec 1779 per sec 2002 per sec

  

  呵呵,速度提高了一倍不是嗎?Allegro庫目前的版本已經使用了我寫的blender函式。

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-952246/,如需轉載,請註明出處,否則將追究法律責任。

相關文章