alpha混合是一种常见的颜色处理,是把源点的颜色值和目标点的颜色值按照一定的算法进行运算,得到一个透明的效果.
alpha混合的基本公式:
result = ALPHA * srcPixel + ( 1 - ALPHA ) * destPixel
其中:
ALPHA:0到1之间的一个数,表示混合时的透明程度,为0的时候结果就是目标点的原值,为1的时候是源点的原值
srcPixel:源点颜色值
destPixel:目标点颜色值
result:结果,将会赋给目标点
这个是最基本的公式,但是我们在写程序的时候可以做一些优化.公式里面有两个乘号,乘法消耗的时钟周期要多得多,我们要想方法去掉它.做一个变换.
alpha混合的改进公式1:
result = ALPHA * ( srcPixel - destPixel ) + destPixel
然后我们也看到ALPHA是一个浮点数,我们可以把它转换成整数,因为一种颜色最多占8bit,所以ALPHA的值也最多到256,那我们把ALPHA的值先乘上256,然后运算的时候再除以256就得到下面的公式:
alpha混合的改进公式2:
result = ALPHA * ( srcPixel - destPixel )/256 + destPixel
其中:
ALPHA是一个0到256的数
有了这个公式,我们的第一份code也就出来了.
alpha混合代码1:
//16位565格式
i = height;???// 混合矩形高度
do
{
j = width; ?// 混合矩形宽度
do
{
sTemp = *((WORD*)lpSour);??// 源点颜色值,16位--WORD类型,(lpSour和lpDest都是BYTE*)
if ( sTemp != sColorKey ) ? // 不是colorkey
{
dTemp = *((WORD*)lpDest); // 目标点
sb = sTemp & 0x1f; ??? // 蓝色分量
db = dTemp & 0x1f;
sg = (sTemp 5) & 0x3f; // 绿色分量
dg = (dTemp 5) & 0x3f;
sr = (sTemp 11) & 0x1f;// 红色分量
dr = (dTemp 11) & 0x1f;
blue = (ALPHA * (sb - db) 8) + db;??// 按照改进公式2运算三个分量
green = (ALPHA * (sg - dg) 8) + dg;
red = (ALPHA * (sr - dr) 8) + dr;
*((WORD*)lpDest) = blue | (green
}
lpDest += 2; // 下一个点,16位占2个字节
lpSour += 2;
}while ( --j 0 );
lpDest += dPadding; // 下一行
lpSour += sPadding;
}while (--i 0);
到此alpha混合也就差不多了,但是不得不说说MMX版本的alpha混合.上面的方式进行alpha混合的时候效率不高,而MMX使用64位的MMX寄存器,一次操作8(byte)/4(word)/2(dword)个数据单元,也就是所谓的"单指令多数据SIMD结构".这样我们就可以一次处理几个点.下面的代码是写给16位565模式的.
因为是16位模式,那么我们可以一次处理4个点,但是因为编译器不会主动的生成MMX代码,所以我们使用inline asm的方式进行.因为我们的点是定义成无符号的word型,不能出现负数,所以我们要修改我们的alpha混合公式:
alpha混合mmx版公式:
result = ( ALPHA * ( ( srcPixel+ 64 ) - destPixel) ) / 256 + destPixel- (ALPHA / 4)
这个公式是这样来的:因为16位模式下每个点的分量最多6位,换成10进制也就是63,那么我们在srcPixel上面加上64然后再减去destPixel,就不会出现负数了.加了之后当然要减回来,那这个公式就是这样的:
result = ( ALPHA * ( ( srcPixel+ 64 -64) - destPixel) ) / 256 + destPixel
然后把紫色的-64提出来也就得到了上面的公式了.
好了我们可以动手写code了,看起来的code应该如下:
alpha混合code2:
MASKRED = 0xF800F800F800F800;???// 三种颜色的掩码的64位扩展
MASKGREEN = 0x07E007E007E007E0;
MASKBLUE = 0x001F001F001F001F;
__int64 ALPHA64, COLORKEY64, ALPHABY4;
__int64 MASKRED, MASKGREEN, MASKBLUE;
__int64 ADD64 = 0x0040004000400040;
_asm
{
movd mm2,ALPHA ????????// ALPHA值放入mm2
punpcklwd mm2,mm2 ?????? // mm2 - 0000 0000 00aa 00aa
punpckldq mm2,mm2 ?????? // mm2 - 00aa 00aa 00aa 00aa,生成alpha的64位扩展
movq ALPHA64,mm2 ???????// 结果放入到 ALPHA64
psrlw mm2, 2 ?????????// 每个ALPHA 除以4
movq ALPHABY4,mm2 ?????? // 放到 ALPHABY4
movd mm4,ColorKey ?????? // ColorKey - mm4
punpcklwd mm4,mm4 ?????? // mm4 - 0000 0000 cccc cccc
punpckldq mm4,mm4 ?????? // mm4 - cccc cccc cccc cccc,生成Colorkey的64位扩展
movq COLORKEY64,mm4 ; ???? // 结果放到 COLORKEY64
}
i = height;
do
{
j = width/4;
????
// 两行写在一起表示假设他们同时在uv管道里面执行
_asm
{
push edi ????????? // 保存edi和esi
push esi
mov edi,lpDest ?????? // edi指向dest缓冲区
mov esi,lpSour; ??????// esi指向sour缓冲区
SPAN_RUN_565:
movq mm7,[edi] ?????? // dest的8 bytes 到 mm7--4个dest点到mm7
movq mm6,[esi] ?????? // sour的8 bytes 到 mm6--4个sour点到mm6
movq mm2,ALPHA64 ????? // ALPHA64 - mm2
movq mm0,mm7 ??????? // 红色 - 复制 mm7 到 mm0,目标点
pand mm0,MASKRED ????? // 红色 - 与上红色掩码 - [0r00 0r00 0r00 0r00],目标点
movq mm1,mm6 ??????? // 红色 - 复制 mm6 到 mm1,源点
pand mm1,MASKRED ????? // 红色 - 与上红色掩码 - [0r00 0r00 0r00 0r00],源点
psrlw mm0,11 ??????? // 红色 - 右移 11 位 - [000r 000r 000r 000r],目标点
movq mm5,mm7 ??????? // 绿色 - 复制 mm7 到 mm5,目标点
psrlw mm1,11 ??????? // 红色 - 右移 11 位 - [000r 000r 000r 000r],源点
paddw mm1, ADD64??????// 红色 - 源点加上64
movq mm3,mm6 ??????? // 绿色 - 复制 mm6 到 mm3,源点
psubsw mm1,mm0 ?????? // 红色 - 减去目标点
pand mm5,MASKGREEN ???? // 绿色 - 与上绿色掩码 - [00g0 00g0 00g0 00g0],目标点
pmullw mm1,mm2 ?????? // 红色 - 乘以ALPHA
pand mm3,MASKGREEN ???? // 绿色 - 与上绿色掩码 - [00g0 00g0 00g0 00g0],源点
psrlw mm5,5 ????????// 红色 - 右移 5 位 - [000g 000g 000g 000g],目标点
psrlw mm3,5 ????????// 红色 - 右移 5 位 - [000g 000g 000g 000g],源点
nop ????????????// 空操作 - 指令配对
paddw mm3, ADD64??????// 绿色 - 源点加上64
psrlw mm1,8 ????????// 红色 - 除以 256 (右移8位)
psubsw mm3,mm5 ?????? // 绿色 - 减去目标点
pmullw mm3,mm2 ?????? // 绿色 - 乘以ALPHA
paddw mm1,mm0 ???????// 红色 - 加上目标点
psubw mm1, ALPHABY4???? // 红色 - 减去alpha的四分之一
psllw mm1,11 ??????? // 红色 - 左移11位,还原[0r00 0r00 0r00 0r00]
movq mm0,mm7 ??????? // 蓝色 - 复制 mm7 到 mm0,目标点
pand mm0, MASKBLUE ???? // 蓝色 - 与上蓝色掩码 - [000b 000b 000b 000b],目标点
psrlw mm3,8 ????????// 绿色 - 除以 256 (右移8位)
paddw mm3,mm5 ???????// 绿色 - 加上目标点
movq mm4, mm6 ???????// 蓝色 - 复制 mm6 到 mm4,源点
psubw mm3, ALPHABY4 ????// 绿色 - 减去alpha的四分之一
pand mm4, MASKBLUE?????// 蓝色 - 与上蓝色掩码 - [000b 000b 000b 000b],源点
psllw mm3,5 ????????// 绿色 - 左移5位,还原[00g0 00g0 00g0 00g0]
paddw mm4, ADD64??????// 蓝色 - 加上64
psubsw mm4,mm0 ?????? // 蓝色 - 减去目标点
por mm1,mm3 ????????// 组合红色和绿色分量
pmullw mm4,mm2 ?????? // 蓝色 - 乘以alpha
movq mm3,COLORKEY64 ????// COLORKEY64 - mm3
psrlw mm4,8 ????????// 蓝色 - 除以256(右移8位)
pcmpeqw mm3,mm6 ??????// 比较 colorKey 和原来的sour,如果相等,则相应的mm3的位为1
paddw mm4,mm0 ???????// 蓝色 - 加上目标点
movq mm5,mm3 ??????? // mm3 - mm5
psubw mm4, ALPHABY4 ????// 蓝色 - 减去alpha的四分之一
por mm1,mm4 ????????// 合成3种颜色
pand mm5,mm7 ??????? // mm5与上目标点 - 取出源点是colorkey的点的目标点的颜色值
pandn mm3,mm1 ???????// mm3取反然后与上mm1,mm1放的是运算结果,这样就把源