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

16位图像Alpha混合的实现(用汇编写的,比MMX还要快)的更多相关文章

  1. DirectDraw打造极速图形引擎(Alpha混合)

    显然DirectDraw是Windows下写2D图形程序的最好选择,虽然Direct3D也可以写,但是没DirectDraw简单方便,特别对于初学者,一来就接触那么多函数和参数总不是件愉快的事,所以我 ...

  2. 【转载】D3D深度测试和Alpha混合

    原文:D3D深度测试和Alpha混合 1.       深度测试 a)         深度缓冲区:屏幕上每个像素点的深度信息的一块内存缓冲区.D3D通过比较当前绘制的像素点的深度和对应深度缓冲区的点 ...

  3. 《逐梦旅程 WINDOWS游戏编程之从零开始》笔记8——载入三维模型&Alpha混合技术&深度测试与Z缓存

    第17章 三维游戏模型的载入 主要是如何从3ds max中导出.X文件,以及如何从X文件加载三维模型到DirextX游戏程序里.因为复杂的3D物体,要用代码去实现,那太反人类了,所以我们需要一些建模软 ...

  4. 【STM32H7教程】第56章 STM32H7的DMA2D应用之刷色块,位图和Alpha混合

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第56章       STM32H7的DMA2D应用之刷色块, ...

  5. D3D中深度测试和Alpha混合的关系

    我在学习D3D的深度测试和Alpha混合的时候,有一些遗憾.书上提供的例子里说一定要先渲染不透明物体,再渲染透明物体,对渲染状态的设置也有特殊要求.我看的很晕.自己查图形学的书,上网找资料,结果还是糊 ...

  6. 【转载】Alpha混合物体的深度排序

    原文:Alpha混合物体的深度排序 先说个题外话, 本来我想解答一下最近Creators Club论坛上经常出现的一个问题, 意外的是在网上竟然找不到什么全面的答案.. 这是个有着复杂答案的简单问题: ...

  7. Alpha混合

    ShaderLab syntax: Blending 混合 Blending is used to make transparent objects. 混合是用来制作透明物体的. When graph ...

  8. Shader第十三讲 Alpha混合

    http://blog.sina.com.cn/s/blog_471132920101d8z5.html Alpha Blending,中文译作Alpha混合Blending就是控制透明的.处于光栅化 ...

  9. C语言的本质(28)——C语言与汇编之用汇编写一个Helloword

    为了更加深入理解C语言的本质,我们需要学习一些汇编相关的知识.作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但是非常重要.因为它能够完成许多其它语言所无法完成的功能.就拿 Linux 内核 ...

随机推荐

  1. java jni 编程

    最近要学习Java JNI 编程. 我使用的是的windows系统.装了一个cygwin. 根据 <JNI 编程规范和指南>. 文件网址: http://wenku.baidu.com/v ...

  2. Spring 从零開始-05

    最终能到Spring的AOP编程了,AOP的概念特别的多.所以须要你在開始之前有点了解,然后通过代码慢慢学习! - 切面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象.事务管 ...

  3. Swift - 委托(delegate)的介绍,及使用样例

    1,委托的说明 委托(delegate)是Cocoa的一个术语,表示将一个对象的部分功能转交给另一个对象. 比如对象A希望对象B知道将要发生或已经发生某件事情,对象A可以把对象B的引用存为一个实例变量 ...

  4. Qt exe图标

    1.首先准备一张ico照片,也可以通过http://www.ico.la/生成: 2.把ico照片拷贝到项目工程下,比如:“pic.ico” 3.在工程下,创建一个文件“myapp.rc”,用txt打 ...

  5. Qt 打包发布 不能动态打开图片显示问题

    刚写完一个图片标注工具, 发现在我电脑可以实时打开照片显示出来,在他人的电脑上就不可以. 原来Qt默认只识别png 具体解决方案: 原地址:http://blog.csdn.net/goodlixue ...

  6. 一个问题:关于类型转换Type Cast(汇编讲解 as 语法)

    问题如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34   ...

  7. 鼠标进入与离开的消息(使用CM_MOUSEENTER来判断是否进入控件)

    unit Unit1; interface uses  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs ...

  8. 微信公 众平台开发,用于个人技术交流,有兴趣的加QQ群432921500

    微信公 众平台开发,用于个人技术交流,有兴趣的加QQ群432921500

  9. 进阶:案例五: Dynamic 创建 Business Graphic

    效果图: step: 无需节点无需UI 1.添加属性 2.代码: method WDDOMODIFYVIEW . DATA:lr_graph TYPE REF TO cl_wd_business_gr ...

  10. BSGS_Baby steps giant steps算法

    BSGS这个主要是用来解决这个题: A^x=B(mod C)(C是质数),都是整数,已知A.B.C求x. 在具体的题目中,C一般是所有可能事件的总数. 解: 设m = ceil(sqrt(C))(ce ...