16位图像Alpha混合的实现(用汇编写的,比MMX还要快)
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还要快)的更多相关文章
- DirectDraw打造极速图形引擎(Alpha混合)
显然DirectDraw是Windows下写2D图形程序的最好选择,虽然Direct3D也可以写,但是没DirectDraw简单方便,特别对于初学者,一来就接触那么多函数和参数总不是件愉快的事,所以我 ...
- 【转载】D3D深度测试和Alpha混合
原文:D3D深度测试和Alpha混合 1. 深度测试 a) 深度缓冲区:屏幕上每个像素点的深度信息的一块内存缓冲区.D3D通过比较当前绘制的像素点的深度和对应深度缓冲区的点 ...
- 《逐梦旅程 WINDOWS游戏编程之从零开始》笔记8——载入三维模型&Alpha混合技术&深度测试与Z缓存
第17章 三维游戏模型的载入 主要是如何从3ds max中导出.X文件,以及如何从X文件加载三维模型到DirextX游戏程序里.因为复杂的3D物体,要用代码去实现,那太反人类了,所以我们需要一些建模软 ...
- 【STM32H7教程】第56章 STM32H7的DMA2D应用之刷色块,位图和Alpha混合
完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第56章 STM32H7的DMA2D应用之刷色块, ...
- D3D中深度测试和Alpha混合的关系
我在学习D3D的深度测试和Alpha混合的时候,有一些遗憾.书上提供的例子里说一定要先渲染不透明物体,再渲染透明物体,对渲染状态的设置也有特殊要求.我看的很晕.自己查图形学的书,上网找资料,结果还是糊 ...
- 【转载】Alpha混合物体的深度排序
原文:Alpha混合物体的深度排序 先说个题外话, 本来我想解答一下最近Creators Club论坛上经常出现的一个问题, 意外的是在网上竟然找不到什么全面的答案.. 这是个有着复杂答案的简单问题: ...
- Alpha混合
ShaderLab syntax: Blending 混合 Blending is used to make transparent objects. 混合是用来制作透明物体的. When graph ...
- Shader第十三讲 Alpha混合
http://blog.sina.com.cn/s/blog_471132920101d8z5.html Alpha Blending,中文译作Alpha混合Blending就是控制透明的.处于光栅化 ...
- C语言的本质(28)——C语言与汇编之用汇编写一个Helloword
为了更加深入理解C语言的本质,我们需要学习一些汇编相关的知识.作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但是非常重要.因为它能够完成许多其它语言所无法完成的功能.就拿 Linux 内核 ...
随机推荐
- DFS深搜——Red and Black——A Knight's Journey
深搜,从一点向各处搜找到全部能走的地方. Problem Description There is a rectangular room, covered with square tiles. Eac ...
- android开发1:安卓开发环境搭建(eclipse+jdk+sdk)
计划折腾折腾安卓开发了,从0开始的确很痛苦,不过相信上手应该也不会太慢.哈哈 一.Android简介 Android 是基于Linux内核的软件平台和操作系统. Android构架主要由3部分组成,l ...
- 阿里云CentOS配置iptables防火墙[转]
虽说阿里云推出了云盾服务,但是自己再加一层防火墙总归是更安全些,下面是我在阿里云vps上配置防火墙的过程,目前只配置INPUT.OUTPUT和FORWORD都是ACCEPT的规则 一.检查iptabl ...
- 免解压版的Mysql的启动脚本,并且执行导入(windows)
@echo off rem ################### set MYSQL_VERSION=mysql-5.5.32-win32 set LOCK=wot.lock rem ####### ...
- OpenProcessToken令牌函数使用方法
>GetCurrentProcessID 得到当前进程的ID OpenProcessToken得到进程的令牌句柄LookupPrivilegeValue 查询进程的权限AdjustTokenPr ...
- CloudStack 4.2 新功能:集成SNMP进行系统监控(原理篇)
作者微博:http://weibo.com/tianchunfeng CloudStack 4.2 版本发布在即,相信不久后对 4.2 版本新功能(共有13个)的介绍会逐渐多起来.因为无论是从架构底层 ...
- POJ - 1422 Air Raid 二分图最大匹配
题目大意:有n个点,m条单向线段.如今问要从几个点出发才干遍历到全部的点 解题思路:二分图最大匹配,仅仅要一条匹配,就表示两个点联通,两个点联通仅仅须要选取当中一个点就可以,所以有多少条匹配.就能够减 ...
- ASP.NET - 在线编辑器(FreeTextBox)
1.首先下载FreeTextBox程序集,再次使用3.3.1 · 官网:http://freetextbox.com/ · 百度云: 2.在程序中引入程序集 3.在工具箱中添加控件. 4.之后拖动控件 ...
- Windows 和 Linux下使用socket下载网页页面内容(可设置接收/发送超时)的代码
主要难点在于设置recv()与send()的超时时间,具体要注意的事项,请看代码注释部分,下面是代码: #include <stdio.h> #include <sys/types. ...
- c#1所搭建的核心基础之委托
本文将对c#1的委托进行详细探索 委托(delegate) 注 delegate:vt.委派代表; 授权给; [法律]债务转移; 委托作用:在恰当的时间执行一系列操作 1.简单委托的构成 声明委 ...