Chinaunix首页 | 论坛 | 博客
  • 博客访问: 318729
  • 博文数量: 71
  • 博客积分: 1450
  • 博客等级: 上尉
  • 技术积分: 762
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-14 13:31
文章分类

全部博文(71)

文章存档

2014年(3)

2013年(8)

2011年(9)

2010年(38)

2009年(13)

我的朋友

分类: LINUX

2009-11-09 12:07:47

http://realchecko.blogspot.com/2009/01/alpha-blend-for-rgb565.html
把 rgb565 轉到 32 bit (但是 order 不是 RGB),然後作 32 bit 的 X alpha 運算。
這樣,就只要作一次乘法。

rgb565 轉到 32 bit 的方法也很聰明,就是把 中間的 G 移出來到 high 16 bit 中:

unsigned int x = rgb565;
x = (x | (x <<16)) & 0x07E0F81F;

算好後,轉回來的方法:

unsigned short rgb565;
rgb565 = (x | (x >> 16));

所以整個運算就是...

#define ALPHA 12 // 0~31
unsigned int bg32,color32;
unsigned short bg = getpixelvalue(x,y);

bg32 = ( bg | bg<<16 ) & 0x07E0F81F;
color32 = (color | color<<16) & 0x07E0F81F;

unsigned int result = (((color32-bg32) * ALPHA)/32 + bg32) & 0x07E0F81F;

setpixelvalue(x,y, (result | result>>16) );

結果 google 一下 0x7E0F81F,, 有一堆...
-------------------------------------------------
http://www.cublog.cn/u/26691/showart_1089631.html
关于16位色在arm cpu上的快速alpha算法
最近在优化一个小程序,其中涉及到了alpha混合的代码,如果是在
台式机上可以通过mmx指令来加速,但是在arm cpu上mmx指令并不普及,所以
采用了一个小技巧来做这个alpha的优化。

alpha混合其实很简单,要做的只是分解源颜色c1,目的颜色c2,然后将颜色分
量r1,g1,b1和r2,g2,b2分别按照公式(clr*alpha+clr*(32-alpha))/32来计算,
最后再组合成一个颜色值即可,可是如此计算,运算量很大速度很慢。
 
所以现在就要用到一个技巧,首先,就16位色来说一般的格式是565,表示rgb
分量的二进制位数,如图1。
 
 
那么我们就可以利用一个32位的变量来把这个颜色的绿色分量提前,变为如图2的格式。
这样每个颜色分量中间就有了进位的空间,也就不用分解这个颜色值了。
然后,将变形完的两个颜色值按照上面公式进行计算,计算完毕再变回565的格式
就完成了一次alpha混合的计算。
 
c语言源代码如下:

__inline void MakeAlpha(WORD* wpSrc, WORD* wpDes, WORD wAlpha)
{
   register DWORD d1; // 计算用的中间变量,声明为寄存器变量快些
   register WORD wa = *wpSrc; // 源颜色 
   register WORD wb = *wpDes; // 目的颜色
   register DWORD alpha = wAlpha; // alpha值,16位色的色深为32级,

                                  //故alpha取值0-32
// (c1-c2)*alpha/32+c2 由(c1*alpha+c2*(32-alpha))/32变形

// 而来,减少了一次乘法运算
// 下面的式子故意写成这样,编译器会把它处理的很好
// 要比这样快一些
// c1 = (((wa << 16) | wa) & 0x7e0f81f);

// 16位变形32位 0x7e...f为二进制的00000111111000001111100000011111
// c2 = (((wb << 16) | wb) & 0x7e0f81f); 
// d1 = ((c1-c2)*alpha)>>5+c2;

// 除以32等于右移5位,但是位移操作要比乘除法快的多,

// 例如:a*320可以写成a*256+a*64=>(a<<8)+(a<<6)

   d1 = (((((((wa << 16) | wa) & 0x7e0f81f) - (((wb << 16) | wb) & 0x7e0f81f)) * alpha) >> 5) + (((wb << 16) | wb) & 0x7e0f81f)) & 0x7e0f81f;
        wa = (d1 & 0xffff0000)>>16; // g...r...b => ..g..
        wb = d1 & 0xffff; // g...r...b => r...b
        *wpDes = wa | wb; // rgb
}

我们用C语言写出来的乘法,编译器只能做部分优化,生成的汇编是由一些移位相加的循环组成的;有时数组元素的寻址也类似这样,所以除了用汇编改写之外也都有这样的优化空间。这个方法可以取得和汇编接近的计算效率,且和CPU无关,便于移植。

 原文地址 http://blog.pdafans.com/?72643/viewspace-1056.html
--------------------------------------------------------------------
http://dev.gameres.com/Program/Visual/2D/64Kalpha.htm
64K 色模式下的快速 Alpha混合算法
在 32/64k 色模式下,由于每个点的 
RGB 值是放在一个字里,以 16bit 色为例,
一般是按 RGB 或 BGR 565 存放. 传统的软件 Alpha
混合算法是先将 RGB 分离出来, 分开运算,然后再合成.
这造成了 16bit 模式下的 alpha 混合比 24bit
模式下慢的现象,但使用 16bit color
真的那么慢吗? 我认为如果不使用 MMX 指令,
15/16 的比 24bit 的快.
因为我们可以使用一个小的技巧来同时计算
RGB. 而 24 bit 颜色,除非使用 MMX 指令,否则必须分开计算
R G B.

先设 color 是 RGB 565 的, 那么按 2 进制看, 这个颜色字是这样分布的:

RRRRR GGGGGG BBBBB
5位 6位 5位

而 386 以上 CPU 都有 32 位的寄存器,我们只需要将 16bit RGB 变形为

00000 GGGGGG 00000 RRRRR 000000 BBBBB
5位 6位 5位 5位 6位 5位
储存在 32 位寄存器中,(就是把绿色提到 前 16 位里) 由于64k 色下颜色深度是 32 级的,所以 alpha 也只用分 32 级就能满足需要. 那么对上面变形过的双字处理,可以同时算 RGB 了. (Color1*Alpha+Color2*(32-Alpha))/32 能不能简化为 (Color1-Color2)*Alpha/32+Color2.? 我思考过这个问题,以为减法将产生负数,这样再算乘法时有可能出问题, 但是经过测试,这样简化似乎又没有问题. 毕竟极小的误差是可以忽略的

最近温习了一下汇编,今天用 Nasm 写了个 C 可调用的 Alpha 混合函数 (32bit 模式, 我针对 Djgpp 写的) 并进行了 Pentium 优化(针对双流水线,有错请指出) 大家看看,有 bug,还能优化或有更快的方法也请一定 btw, 上面提到的化简没有体现到下面的程序中, 而且,使用乘法本身是个错误. 只是看看吧 ;-) 如果你想实际运用,请参考 Allegro 的做法.

; 对 16bit 的 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)
; (~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,ax ; 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函数。
阅读(3655) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

dqwjack2009-11-20 17:08:10

http://blog.codingnow.com/cloud/CodeCollection?show_comments=1 RGB565 的 alpha 混合 unsigned short alpha_blender(unsigned int x,unsigned int y,unsigned int alpha) { x = (x | (x<<16)) & 0x7E0F81F; y = (y | (y<<16)) & 0x7E0F81F; unsigned int result = ((x - y) * alpha / 32 + y) & 0x7E0F81F; return (unsigned short)((result&0xFFFF) | (result>>16)); } --------------------------------------------------------------------------------------- http://blog.csdn.net/igame/archive/2007/09/12/178177