在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)
;
(http://member.netease.com/~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函数。