Posted on 2010-06-09 14:34
莫失莫忘 閱讀(248)
評(píng)論(0) 編輯 收藏 引用
顯然DirectDraw是Windows下寫2D圖形程序的最好選擇,雖然Direct3D也可以寫,但是沒DirectDraw簡單方便,特別對(duì)于初學(xué)者,一來就接觸那么多函數(shù)和參數(shù)總不是件愉快的事,所以我的文章主要結(jié)合我做的工作,談?wù)凞irectDraw編程中一些比較關(guān)鍵的技術(shù),大多是我自己想出來的。我想先聲明,我的文章可以任意轉(zhuǎn)載,源代碼可以任意使用和修改。
由于我是業(yè)余時(shí)間寫的文章,所以只能每次發(fā)表一篇,希望我的工作可以為大家的游戲增光添彩,同時(shí)我的文章主要面向有基本C++,DirectDraw,匯編和MMX編程經(jīng)驗(yàn)的朋友,如果你對(duì)這些了解不夠,請(qǐng)先學(xué)習(xí)一下再閱讀。也歡迎大家和我交流,我的QQ是35830152,EMAIL:EUHO@SINA.COM。
作為第一篇,我想先談?wù)凙lpha混合的問題。這里32位色的圖形模式我們不考慮,因?yàn)榧记刹⒉欢啵加蔑@存和內(nèi)存大,實(shí)際應(yīng)用的也不多。我們把焦點(diǎn)放在16位色的模式上。我們把源點(diǎn)C2和Alpha通道點(diǎn)C1用Alpha混合,混合后得到點(diǎn)C,如果Alpha取0~1,公式如下:
C = C2*Alpha + C1*(1-Alpha)
如果Alpha取0~32,公式如下:
C = (C2*Alpha + C1*(1-Alpha))>>5
每個(gè)點(diǎn)由R,G,B 3個(gè)分量組成,所以上面的運(yùn)算要分別對(duì)每個(gè)分量進(jìn)行計(jì)算,如果整體計(jì)算,由于進(jìn)位的關(guān)系我們會(huì)得到錯(cuò)誤的結(jié)果。我們只考慮用得較多的565格式,即16位的顏色值為RRRRRGGGGGGBBBBB,555格式原理是一樣的。顯然我們每次處理一個(gè)點(diǎn)似乎只能按照“拆分-分別運(yùn)算-拆分”來寫代碼,但是這樣是低效的,想想1024*768模式下運(yùn)算一幀要進(jìn)行多少次運(yùn)算,一定快不到哪里去。
Intel有段很長的代碼,我沒仔細(xì)看,也沒試驗(yàn),總覺得不太可靠(呵呵)。還看了GameRes上的一些相關(guān)文章,還是有值得參考的地方,就是覺得看了還是有些茫然。
下面說我的算法,首先說明這個(gè)快速算法是針對(duì)每個(gè)Alpha值建立一個(gè)函數(shù)進(jìn)行運(yùn)算,如果在一個(gè)函數(shù)里實(shí)現(xiàn)任意Alpha的運(yùn)算,一次只能運(yùn)算2個(gè)點(diǎn),而且匯編代碼是26行,而且有2次乘法,也用到了部分MMX加速。經(jīng)過針對(duì)每一級(jí)Alpha的優(yōu)化處理,每次處理4個(gè)點(diǎn),代碼只要8行左右,移位代替了乘法運(yùn)算,完全發(fā)揮了MMX的威力。我只做了17級(jí)變換,0級(jí)和17級(jí)不用做,1到15原理一樣,只有少少的不同,現(xiàn)在我舉例半透明的算法,其他大家可以自己實(shí)現(xiàn),有問題也可以和我交流。
Alpha運(yùn)算中每個(gè)點(diǎn)3個(gè)色素,每個(gè)色素都要按上面那個(gè)公式運(yùn)算,也就是每個(gè)色素要做2次乘法和一次加發(fā),盡管可以變換一下不做浮點(diǎn)運(yùn)算,但性能又能提高多少?我先講一下我算法的一個(gè)基本原理,即“任意分組移位”,意思就是把一個(gè)數(shù)中分為N組,每組位數(shù)并不要求相同,我們用一次移位和一次與運(yùn)算就能做到好像是每個(gè)分組移位而互不影響的效果。比半透明下Alpha=0.5,換成移位就是>>1,我們先把C右移一位,然后AND 一個(gè)2進(jìn)制的數(shù)0111101111101111(0x7BEF),就完成了3個(gè)色素同時(shí)*0.5的運(yùn)算,簡單吧。
代碼相信大家很容易就看懂了,大家把匯編部分和自己的程序結(jié)合就可以了,只要提供一些參數(shù),比如頁面數(shù)據(jù)指針和長度高度等資料.下次我會(huì)發(fā)布帶Colorkey和Clip功能的代碼,同樣是MMX處理的,而且不用if(這會(huì)大大降低流水線的效率).以后還會(huì)介紹動(dòng)態(tài)光源,灰度圖,動(dòng)畫控制等高級(jí)主題,歡迎大家指導(dǎo),由于水平和打字原因,可能文章中會(huì)有錯(cuò)誤,請(qǐng)諒解.
下面是任意Alpha的混合運(yùn)算
BOOL
CAresMaterial::DrawAlpha( LONG X, LONG Y, LPRECT pRect, BYTE Alpha )
{
unsigned __int16 *pSrc, *pDest;
unsigned __int32 A, PA;
unsigned __int16 Width, Height;
unsigned __int32 D1, D2;
RECT Rect;
A = Alpha & 0x1F;
PA = 0x1F - A;
Width = (unsigned __int16)(pRect->right - pRect->left + 1);
Height = (unsigned __int16)(pRect->bottom - pRect->top + 1);
D1 = (m_Desc.dwPitch - Width + 1)<<1 ;
D2 = (m_Desc.pAres->GetScreenPitch() - Width + 1)<<1 ;
SetRect( &Rect, X, Y, X+Width-1, Y+Height-1 );
m_Desc.pAres->BackToDILayer( &Rect );
pSrc = m_Desc.pData + pRect->top*m_Desc.dwPitch + pRect->left;
pDest = m_Desc.pAres->GetDILayerData() + Y*m_Desc.pAres->GetScreenPitch() + X;
__asm
{
mov esi,pSrc
mov edi,pDest
movd mm2,A
movd mm3,PA
mov cx,Height
shl ecx,16
mov cx,Width
LOOPA:
ror ecx,16
dec cx
jz DONE
ror ecx,16
LOOPB:
dec cx
jz NEXTLINE
//Process one point
mov ax,[esi]
mov dx,ax
shl eax,16
mov ax,dx
and eax,0x7E0F81F
movd edx,mm2
mul edx
movd mm0,eax
mov ax,[edi]
mov dx,ax
shl eax,16
mov ax,dx
and eax,0x7E0F81F
movd edx,mm3
mul edx
movd mm1,eax
paddd mm0,mm1
psrlq mm0,5
movd eax,mm0
and eax,0x7E0F81F
mov edx,eax
shr edx,16
or eax,edx
mov [edi],ax
inc esi
inc edi
inc esi
inc edi
jmp LOOPB
NEXTLINE:
add esi,D1
add edi,D2
mov cx,Width
jmp LOOPA
DONE:
emms
}
m_Desc.pAres->DILayerToBack( &Rect );
return TRUE;
}
下面是半透明Alpha的混合運(yùn)算
void
CAresMaterial::DrawAlpha1( LONG X, LONG Y, LPRECT pRect )
{
unsigned __int16 *pSrc, *pDest;
unsigned __int16 Width, Height, DW, DLeft;
unsigned __int32 D1, D2;
static unsigned __int64 MASKER = 0x7BEF7BEF7BEF7BEF;
RECT Rect;
Width = (unsigned __int16)(pRect->right - pRect->left);
Height = (unsigned __int16)(pRect->bottom - pRect->top + 1 );
pSrc = m_Desc.pData + pRect->top*m_Desc.dwPitch + pRect->left;
pDest = m_Desc.pAres->GetBackData() + Y*m_Desc.pAres->GetScreenPitch() + X;
DLeft = (Width % 4) + 1;
DW = (Width>>2) + 1;
D1 = (m_Desc.dwPitch - Width)<<1 ;
D2 = (m_Desc.pAres->GetScreenPitch() - Width)<<1 ;
SetRect( &Rect, X, Y, X+Width, Y+Height-1 );
__asm
{
mov esi,pSrc
mov edi,pDest
mov bx,DLeft
mov cx,Height
shl ecx,16
mov cx,DW
LOOPA:
ror ecx,16
dec cx
jz DONE
ror ecx,16
LOOPB:
dec cx
jz ENDLINE
//Process four points once
movq mm0,[esi]
movq mm1,[edi]
psrlq mm0,1
psrlq mm1,1
pand mm0,MASKER
pand mm1,MASKER
paddw mm0,mm1
movq [edi],mm0
add esi,8
add edi,8
jmp LOOPB
ENDLINE:
dec bx
jz NEXTLINE
mov ax,[esi]
mov dx,[edi]
shr ax,1
shr dx,1
and ax,0x7BEF
and dx,0x7BEF
add ax,dx
mov [edi],ax
inc esi
inc esi
inc edi
inc edi
jmp ENDLINE
NEXTLINE:
add esi,D1
add edi,D2
mov cx,DW
mov bx,DLeft
jmp LOOPA
DONE:
emms
}
}