Alpha 混合的算法很简单,基于下面的公式就可以实现:

D := A * (S - D) / 255 + D

D 是目标图像的像素,

S 是源图像的像素

A 是 Alpha 值, 0 为全透明, 255 为不透明。

下面是 16 位 565 格式的混合算法的实现,首先用最简单的方式实现,即逐个像素的处理:

// 一次处理一个像素,比较简单,但速度较慢

procedure AlphaBlend656(BmpDst, BmpSrc: TBitmap; Alpha: Byte);

var

i, j, W, H: Integer;

pSrc, pDst: PWord;

wSR, wSG, wSB: Word;

wDR, wDG, wDB: Word;

begin

// 确定高宽

if BmpDst.Width > BmpSrc.Width then

W := BmpSrc.Width

else

W := BmpDst.Width;

if BmpDst.Height > BmpSrc.Height then

H := BmpSrc.Height

else

H := BmpDst.Height;

for i := 0 to H - 1do

begin

pSrc := BmpSrc.ScanLine[i];

pDst := BmpDst.ScanLine[i];

for j := 0 to W - 1 do

begin

// D := A * (S - D) / 255 + D

wSR := (pSrc^ shr 11);

wSG := (pSrc^ shr 5) and $3F;

wSB := pSrc^ and $1F;

wDR := (pDst^ shr 11);

wDG := (pDst^ shr 5) and $3F;

wDB := pDst^ and $1F;

pDst^ := (((Alpha * (wSR - wDR) shr 8) + wDR) shl 11) or

(((Alpha * (wSG - wDG) shr 8) + wDG) shl 5) or

((Alpha * (wSB - wDB) shr 8) + wDB);

Inc(pSrc);

Inc(pDst);

end;

end;

end;

实现起来很简单,但速度比较慢,其实存在着一次处理两个像素的算法,下面是代码:

// 一次处理两个像素 , 所以速度是 AlphaBlend656 的 2 倍

procedure AlphaBlend656Fast(BmpDst, BmpSrc: TBitmap; Alpha: Byte);

var

i, j, W, H: Integer;

pSrc, pDst: PWord;

dwSR, dwSG, dwSB: LongWord;

dwDR, dwDG, dwDB: LongWord;

dwAdd64 : LongWord;

dwAlphaOver4 : LongWord;

odd: Boolean;

begin

// 确定高宽

if BmpDst.Width > BmpSrc.Width then

W := BmpSrc.Width

else

W := BmpDst.Width;

if BmpDst.Height > BmpSrc.Height then

H := BmpSrc.Height

else

H := BmpDst.Height;

dwAdd64 := 64 or ( 64 shl 16 );

dwAlphaOver4 := ( Alpha shr 2 ) or ( ( Alpha shr 2 ) shl 16 );

if (W and $01) = 1 then

begin

odd := True;

W := (W - 1) shr 1;

end

else begin

odd := False;

W := W shr 1;

end;

for i := 0 to H - 1 do

begin

pSrc := BmpSrc.ScanLine[i];

pDst := BmpDst.ScanLine[i];

for j := 0 to W - 1 do

begin

// D := A * (S - D) / 255 + D

dwSR := (PLongWord(pSrc)^ shr 11) and $001F001F;

dwSG := (PLongWord(pSrc)^ shr 5) and $003F003F;

dwSB := PLongWord(pSrc)^ and $001F001F;

dwDR := (PLongWord(pDst)^ shr 11) and $001F001F;

dwDG := (PLongWord(pDst)^ shr 5) and $003F003F;

dwDB := PLongWord(pDst)^ and $001F001F;

PLongWord(pDst)^ := ((((Alpha * (dwSR + dwAdd64 - dwDR)) shr 8) + dwDR - dwAlphaOver4) and $001F001F) shl 11 or

((((Alpha * (dwSG + dwAdd64 - dwDG)) shr 8) + dwDG - dwAlphaOver4 ) and $003F003F) shl 5 or

(((Alpha * (dwSB + dwAdd64 - dwDB)) shr 8) + dwDB - dwAlphaOver4 ) and $001F001F;

Inc(pSrc, 2);

Inc(pDst, 2);

end;

if odd then

begin

dwSR := (pSrc^ shr 11);

dwSG := (pSrc^ shr 5) and $3F;

dwSB := pSrc^ and $1F;

dwDR := (pDst^ shr 11);

dwDG := (pDst^ shr 5) and $3F;

dwDB := pDst^ and $1F;

pDst^ := Word((((Alpha * (dwSR - dwDR) shr 8) + dwDR) shl 11) or

(((Alpha * (dwSG - dwDG) shr 8) + dwDG) shl 5) or

((Alpha * (dwSB - dwDB) shr 8) + dwDB));

Inc(pSrc);

Inc(pDst);

end;

end;

end;

不过这还不够快,基本 MMX 指令的实现可以一次处理 4 个像素,下面是代码:

// 利用 MMX 优化指令,一次可以处理 4 个像素,因此速度应该是 AlphaBlend656 的 4 倍

procedure AlphaBlend656MMX(BmpDst, BmpSrc: TBitmap; Alpha: Byte);

var

i, j, W, H, Leave: Integer;

pSrc, pDst: PWord;

MaskR, MaskG, MaskB, Alpha64: Int64;

wSR, wSG, wSB: Word;

wDR, wDG, wDB: Word;

begin

// 确定高宽

if BmpDst.Width > BmpSrc.Width then

W := BmpSrc.Width

else

W := BmpDst.Width;

if BmpDst.Height > BmpSrc.Height then

H := BmpSrc.Height

else

H := BmpDst.Height;

Leave := W and 3;             // 剩余的像素

W := W shr 2;                 // 一次处理 4 个像素,因此取 W 整除 4 的值

// 提取 RGB 通道的掩码

MaskR := $f800f800f800f800;

MaskG := $07e007e007e007e0;

MaskB := $001f001f001f001f;

// Alpha 值扩展到 64 位

Alpha64 := Alpha;

Alpha64 := Alpha64 or (Alpha64 shl 16) or (Alpha64 shl 32) or (Alpha64 shl 48);

for i := 0 to H - 1do

begin

pSrc := BmpSrc.ScanLine[i];

pDst := BmpDst.ScanLine[i];

asm

push    ecx               // 保存寄存器

mov     ecx, W            // 设宽度

cmp     ecx, 0            // 宽度是否为 0

jz      @@exit565         // 如果宽度为 0 ,结束

push    esi

push    edi

mov      esi, pSrc         // 开始处理

mov     edi, pDst

@@blend565_4:

{ mmx 的作用:

mm0: red target value

mm1: red source value

mm2: green target value

mm3: green source value

mm4: blue target value

mm5: blue source value

mm6: original target pixel

mm7: original source pixel

D := A * (S - D) / 255 + D

}

movq    mm6, [edi]

movq    mm7, [esi]

movq    mm0, mm6

pand    mm0, MaskR        // 提取目标的 R 通道

movq    mm1, mm7

pand    mm1, MaskR        // 提取源的 R 通道

psrlw   mm0, 11           // 右移到最低位,便于接下来的计算

psrlw   mm1, 11

psubw   mm1, mm0          // SrcRed := SrcRed - DestRed

pmullw  mm1, Alpha64      // SrcRed := SrcRed * Alpha

psraw   mm1, 8            // SrcRed := SrcRed div 8

paddw   mm1, mm0          // SrcRed := SrcRed + DestRed

psllw   mm1, 11           // 左移回原来的位置,此已经 R 通道混合已经完毕

movq    mm2, mm6

pand    mm2, MaskG        // 提取目标的 G 通道

movq    mm3, mm7

pand    mm3, MaskG        // 提取源的 G 通道

psrlw   mm2, 5            // 右移到最低位,便于接下来的计算

psrlw   mm3, 5

psubw   mm3, mm2          // SrcGreen := SrcGreen - DestGreen

pmullw  mm3, Alpha64      // SrcGreen := SrcGreen * Alpha

psraw   mm3, 8            // SrcGreen := SrcGreen div 8

paddw   mm3, mm2          // SrcGreen := SrcGreen + DestGreen

psllw   mm3, 5            // 左移回原来的位置,此已经 G 通道混合已经完毕

movq    mm4, mm6

pand    mm4, MaskB        // 提取目标的 B 通道

movq    mm5, mm7

pand    mm5, MaskB        // 提取源的 B 通道

psubw   mm5, mm4          // SrcBlue := SrcBlue - DestBlue

pmullw  mm5, Alpha64      // SrcBlue := SrcBlue * Alpha

psraw   mm5, 8            // SrcBlue := SrcBlue div 8

paddw   mm5, mm4          // SrcBlue := SrcBlue + DestBlue ,此已经 B 通道混合已经完毕

por     mm1, mm3          // 合成像素

por     mm1, mm5

movq    [edi], mm1        // 赋给目标

add     esi, 8            // 下 4 个像素

add     edi, 8

dec     ecx

jnz      @@blend565_4

mov     pSrc, esi

mov     pDst, edi

pop     edi

pop     esi

emms

@@exit565:

pop     ecx

end;

// 处理剩下的像素

for j := 0 to Leave - 1 do

begin

wSR := (pSrc^ shr 11);

wSG := (pSrc^ shr 5) and $3F;

wSB := pSrc^ and $1F;

wDR := (pDst^ shr 11);

wDG := (pDst^ shr 5) and $3F;

wDB := pDst^ and $1F;

pDst^ := (((Alpha * (wSR - wDR) shr 8) + wDR) shl 11) or

(((Alpha * (wSG - wDG) shr 8) + wDG) shl 5) or

((Alpha * (wSB - wDB) shr 8) + wDB);

Inc(pSrc);

Inc(pDst);

end;

end;

end;

下面是这三个函数的速度比较,目标图像是 600*450 的 16 位位图,源图像是 399*532 的 16 位位图,分别进行了 1000 次混合,结果如下:

AlphaBlend656 :           4516

AlphaBlend656Fast :       2562

AlphaBlend656MMX :        1234

没有意外, MMX 版本比普通的快了近 4 倍

对于图像处理的优化有两个比较重要的点:

1、 尽量用位移代替乘除。

2、 一次能够同时处理多个像素,利用 MMX 指令可以做到这一点。

最后是代码:

https://files.getdropbox.com/u/524963/AlphaBlend16_565.rar

http://blog.csdn.net/linzhengqun/article/details/4269259

最新文章

  1. Spring MVC类型转换
  2. OpenGL变换
  3. (十) 一起学 Unix 环境高级编程 (APUE) 之 线程控制
  4. RecyclerView+CardView简单使用
  5. ckplayer播放器去掉右边的开关灯分享插件
  6. IOS开发之——登录加密也许用到的,反转字符串
  7. ZeroMQ安装
  8. load url from future 解释
  9. C++Premer Plus学习(五)——函数探幽
  10. OSAL
  11. Microsoft HoloLens 技术解谜(下)
  12. .net c# 正则表达式 平衡组/递归匹配
  13. select的onchange事件获取不了option的value
  14. Intent Flag实际项目 -- 超时跳转登录界面并清理前面所有activity
  15. Myeclipse和windows调节成护眼色
  16. HDU1789 Doing Homework again
  17. hihocoder1388 Periodic Signal
  18. Visual studio 编辑combobox程序卡死的问题
  19. ssh连接远程主机执行脚本的环境变量问题
  20. MongoDB日志文件过大的解决方法

热门文章

  1. USACO 2005 January Gold The Wedding Juicer
  2. GTest交流与经验总结
  3. JAVA多态学习3
  4. PHP - 操作MySQL数据库
  5. wkhtmltopdf 生成pdf
  6. Ch03 视图基础
  7. innerHeight与clientHeight、innerWidth与clientWidth、scrollLeft与pageXOffset等属性
  8. iOS依据字符串计算UITextView高度
  9. 【linux】内核编译
  10. Endnote从头开始五:修改output style(转载)