搜索
bottom↓
回复: 45

今日想优化下透明显示的算法,用结构体分离(合并)RGB值,我的妈呀,竟然比移位算法效率还要

[复制链接]

出0入0汤圆

发表于 2011-12-20 16:04:52 | 显示全部楼层 |阅读模式
方法一:

/****************************************************************************
*函数名:MixRGB
*功能  : 将rgb值以5:6:5的形式合并成16位值
*输入  :R 红色分量,G绿色分量,B蓝色分量
*创建时间:2011.11.23
*说明  :
*****************************************************************************/
u16 MixRGB(u8 R,u8 G,u8 B)  //这函数的功能就是将5:6:5的RGB值合并陈一个16位的值
{
        u16 color = 0;
               
        color |= R & 0x001f ;
        color |= (G & 0x003f) << 5;
        color |= (B & 0x001f) << 11;          
       
        return color;       
}
/****************************************************************************
*函数名:AlphaBlend
*功能  : 将两个颜色值混合
*输入  :新fr_c叠加上去的颜色值,bk_c背景颜色基值,alpha透明度:0为不透明,即bk_c值。255为全透明,即fr_c值
*创建时间:2011.11.23
*说明  :参考了刘伟大侠的大作,阿尔法混合
*****************************************************************************/
u16 AlphaBlend(u16 fr_c,u16 bk_c,u8 alpha)
{       
        u16 color = 0;
       

        color = MixRGB(((fr_c & 0x001f)         * alpha + (bk_c & 0x001f)         * (0xff-alpha) ) >> 8,
                                  ( ((fr_c >> 5) & 0x003f)  * alpha + ((bk_c >> 5) & 0x003f)  * (0xff-alpha) ) >> 8,
                                  ( ((fr_c >> 11) & 0x001f) * alpha + ((bk_c >> 11) & 0x001f) * (0xff-alpha) ) >> 8);
        return color;
                                       
}


方法二:

定义一个 共用体 + 结构体 的方法可以实现RGB值的合并与分离
typedef  union
{
        struct
        {
                u16 r : 5;
                u16 g : 6;
                u16 b : 5;
        }rgb;

        u16 rgb565;

} color565;

/****************************************************************************
*函数名:AlphaBlend
*功能  : 将两个颜色值混合
*输入  :新fr_c叠加上去的颜色值,bk_c背景颜色基值,alpha透明度:0为不透明,即bk_c值。255为全透明,即fr_c值
*创建时间:2011.11.23
*说明  :
*****************************************************************************/
u16 AlphaBlend(u16 fr_c,u16 bk_c,u8 alpha)
{       
        color565 color, color_fr_c,color_bk_c ;

        color_fr_c.rgb565 = fr_c;
        color_bk_c.rgb565 = bk_c;

        color.rgb.r = ( color_fr_c.rgb.r * alpha + color_bk_c.rgb.r * (0xff-alpha) ) >> 8;
        color.rgb.g = ( color_fr_c.rgb.g * alpha + color_bk_c.rgb.g * (0xff-alpha) ) >> 8;
        color.rgb.b = ( color_fr_c.rgb.b * alpha + color_bk_c.rgb.b * (0xff-alpha) ) >> 8;
       
        return color.rgb565 ;
                                       
}

上面两种方法均能实现颜色的叠加

可以看出:第二种方法非常简洁,仅三行就可以代替第一种方法的两条函数,而且可读性很强吧。

但是这不是重点,我写第二种方法的用意是想提高算法的效率的,结果第二种方法的效率比第一种方法还要低,虽然不是低很多,却很出乎我的意料。

第二种方法就直接操作结构体里面成员,最后就和 rgb565 组合成一个共用体,这样就可以实现分离或合并了。而第一种方法用了大量了移位运算和&运算,却效率还高,真搞不懂,请高人来回答!!

阿莫论坛20周年了!感谢大家的支持与爱护!!

曾经有一段真挚的爱情摆在我的面前,我没有珍惜,现在想起来,还好我没有珍惜……

出0入0汤圆

发表于 2011-12-20 16:13:53 | 显示全部楼层
回复【楼主位】danielmi 俊俊
-----------------------------------------------------------------------

是位对齐的问题!

以8位机为例,如果指令自己的最小存取单位是8bit,你共用体里小于8bit连续存储
因为它最小的指令只能操作8位,当你从中取5、6、5时,编译器也会编译出移位指令!

最后的结果是,编译出来的效率还没你自己写的高。

但是如果RGB是各占8位的话,方法二的会明显快于一的

以上所说,不同平台的编译环境可能有差异

出0入0汤圆

 楼主| 发表于 2011-12-20 16:42:41 | 显示全部楼层
回复【1楼】yrloy 断雪
-----------------------------------------------------------------------

感谢你的回答,我用得是stm32 + MDK,那这个到底是字节对齐还是字对齐呢,他是由编译器决定的还是由目标器件决定的??

出0入0汤圆

发表于 2011-12-20 16:56:57 | 显示全部楼层
color_fr_c.rgb.b * alpha// 这个是16bit*8bit
((fr_c >> 11) & 0x001f) * alpha //这个是8bit*8bit

出0入0汤圆

发表于 2011-12-20 18:16:44 | 显示全部楼层
50%50%多好,屏蔽rgb最低位再右移一位相加就成了,不用拆开合并rgb

出0入0汤圆

 楼主| 发表于 2011-12-21 11:55:11 | 显示全部楼层
回复【4楼】hemjidn 捱多年
-----------------------------------------------------------------------

不是很懂,请问可以详细的指教下吗,谢谢!!

出0入0汤圆

发表于 2011-12-21 12:43:41 | 显示全部楼层
这个问题是字节对齐的问题,stm32是32位的,你结构体中的位域是8位存储的,所以这样会导致效率低下,我觉得你还是用define写成个do{}while(0)的形式,这避免用函数,效率应该会高一点,如果你喜欢用函数,加个inline试试!

出0入0汤圆

发表于 2011-12-21 13:10:07 | 显示全部楼层
回复【2楼】danielmi 俊俊
回复【1楼】yrloy 断雪
-----------------------------------------------------------------------
感谢你的回答,我用得是stm32 + mdk,那这个到底是字节对齐还是字对齐呢,他是由编译器决定的还是由目标器件决定的??
-----------------------------------------------------------------------

测试发现是字对齐的!

出0入0汤圆

发表于 2011-12-21 13:15:26 | 显示全部楼层
#pragma pack(1)

typedef struct

{

    ....,
    ....,

}kk;

#pragma pack()
强制设置为1字节对齐。你这样设置之后再试试!最后一个不能忘,避免其他的也设置为1字节对齐,造成其他代码效率低下

出0入0汤圆

 楼主| 发表于 2011-12-21 14:31:51 | 显示全部楼层
回复【8楼】aaxixi
-----------------------------------------------------------------------

感谢,按照你说得来做也没有见效,估计都是和一楼所说的,我的位域定义的都不足八位,存储都要经过一定的运算才能得出数据,估计定义为bit对齐就有效了

出0入0汤圆

 楼主| 发表于 2011-12-21 14:50:55 | 显示全部楼层
回复【6楼】aaxixi
-----------------------------------------------------------------------

之前看到过__inline这关键字,但我不知道有何用,刚刚查了一下,好像是将函数定义成类似于define的形式是吧??

出0入0汤圆

发表于 2011-12-21 15:21:25 | 显示全部楼层
回复【楼主位】danielmi  俊俊
-----------------------------------------------------------------------

不管用什么语句,数据肯定是要倒腾过去的,看了下cortex m3的指令,视乎可以
bfi color 0..5
color <<=6;
bfi color 0..6
color <<=5
bfi color o..5
汇编5条就可以了

出0入0汤圆

发表于 2011-12-21 15:45:55 | 显示全部楼层
你的算法本身可以更优化,下面是我做的


u16 make_alpha16(u32 c1, u32 c2, u8 al)
{
    c1 = (c1 | c1<<16) & 0x07e0f81f;
    c2 = (c2 | c2<<16) & 0x07e0f81f;
    c1 = ((c2-c1)*al/32 + c1) & 0x07e0f81f;
    return (u16)(c1|(c1>>16));
}

c1 c2是16位色565格式 ,al是alpha透明度,我设的是0~32级

出0入0汤圆

 楼主| 发表于 2011-12-21 21:07:08 | 显示全部楼层
回复【12楼】snoopyzz
你的算法本身可以更优化,下面是我做的
u16 make_alpha16(u32 c1, u32 c2, u8 al)
{
    c1 = (c1 | c1&lt;&lt;16) &amp; 0x07e0f81f;
    c2 = (c2 | c2&lt;&lt;16) &amp; 0x07e0f81f;
    c1 = ((c2-c1)*al/32 + c1) &amp; 0x07e0f81f;
    return (u16)(c1|(c1&gt;&gt;16));
}
c1 c2是16位色565格式 ,al是alpha透明度,我设的是0~32级
-----------------------------------------------------------------------

请问这是什么原理呢,可不可以解释下??

出0入0汤圆

 楼主| 发表于 2011-12-21 21:13:49 | 显示全部楼层
回复【12楼】snoopyzz
-----------------------------------------------------------------------

刚刚试了下,的确是快了不少,不过刷完一屏要70多MS(屏分辨率是400*240),还没能达到要求啊,继续请大侠发表意见!!

出0入0汤圆

发表于 2011-12-21 21:46:47 | 显示全部楼层
c1回复【5楼】danielmi  俊俊
-----------------------------------------------------------------------

c1=(c1>>1)&0x7bef
c2=(c2>>1)&0x7bef
c=c1+c2
很简单只是适用范围太少

出0入0汤圆

发表于 2011-12-21 22:25:58 | 显示全部楼层
c1 = (c1 | c1<<16) & 0x07e0f81f;

    c2 = (c2 | c2<<16) & 0x07e0f81f;

   //把g值移到高位 c1 = ((c2-c1)*al/32 + c1) & 0x07e0f81f;//计算透明

    return (u16)(c1|(c1>>16));
//把g值移回原处

根据原理只需要把差值透明运算一次
验证不通过再想想

出350入1925汤圆

发表于 2011-12-22 00:06:08 | 显示全部楼层
记号。。

出0入0汤圆

发表于 2011-12-22 00:22:38 | 显示全部楼层
第一:5:6:5是不提倡的,用8:8:8最后再整合。
第二:引用的时候最好定义几个临时变量,先把指针的数据copy到临时变量,然后计算,然后再把结果copy回去,
         看似多了许多步骤,不过在目前编译器的智能分析的程度下,这样写能大大提高速度。
原因是:指针指向的ROM区间的每次存取都要几个周期,而且RISC的mcu在存取指针数据的时候比CISC的构架会多好几步,
            这样大大降低了速度,另外还浪费了高速寄存器,只要把数据给arm的R组寄存器,这样不仅存取速度提高,
           而且读写数据只需一步,也充分利用了arm的寄存器构架,如果是堆栈式构架可能就不如寄存器式构架的速度提升明显。

出0入0汤圆

发表于 2011-12-22 00:45:56 | 显示全部楼层
12楼的算法好像也有误差……
洗洗睡明天继续

出0入0汤圆

 楼主| 发表于 2011-12-22 08:52:25 | 显示全部楼层
回复【15楼】hemjidn 捱多年
c1回复【5楼】danielmi  俊俊
-----------------------------------------------------------------------
c1=(c1&gt;&gt;1)&amp;0x7bef
c2=(c2&gt;&gt;1)&amp;0x7bef
c=c1+c2
很简单只是适用范围太少
-----------------------------------------------------------------------

这算法就是半透明是吧!!!

出0入0汤圆

 楼主| 发表于 2011-12-22 08:58:58 | 显示全部楼层
回复【16楼】hemjidn 捱多年
-----------------------------------------------------------------------

算是理解了,原来copy低16位到高16位就是为了取出g值,这想法不错,在32位机中更好。至于计算透明值的那步,原理上是和我的一样的,只是在我的基础上作了四则运算,也就是人算好了就不用处理器去算了,从而达到提高效率的效果!

出0入0汤圆

发表于 2011-12-22 08:59:30 | 显示全部楼层
我在12L做的允许透明度是0~32
想要0~255的需要把/32改成/255
当然/255的话,除法必竟慢些,允许误差的话,写成/256或>>8好了

出0入0汤圆

 楼主| 发表于 2011-12-22 09:02:35 | 显示全部楼层
回复【18楼】hzr0071
-----------------------------------------------------------------------

直接将参与运算的变量声明为register,是否达到你说的效果!!

出0入0汤圆

发表于 2011-12-22 09:03:34 | 显示全部楼层
回复【22楼】danielmi  俊俊
回复【16楼】hemjidn 捱多年
-----------------------------------------------------------------------
算是理解了,原来copy低16位到高16位就是为了取出g值,这想法不错,在32位机中更好。至于计算透明值的那步,原理上是和我的一样的,只是在我的基础上作了四则运算,也就是人算好了就不用处理器去算了,从而达到提高效率的效果!
-----------------------------------------------------------------------

我在12L算法的本质原理是把16位扩充到32位...并且留出能够进位的空间来
0x7e0f81f = 0b111111000001111100000011111
原理本身是把rrrrrggggggbbbbb变形成gggggg00000rrrrr000000bbbbb
然后就可以通过一次aplha运算就得到最终结果,当然...这样做要在32位机上才有最高效率

出0入0汤圆

 楼主| 发表于 2011-12-22 09:06:21 | 显示全部楼层
回复【25楼】snoopyzz
-----------------------------------------------------------------------

嗯,我昨天在你的函数前面用__inline去修饰,可以达到50MS一屏了!!

出0入0汤圆

发表于 2011-12-23 13:16:33 | 显示全部楼层
不能除256,当al<8时就会杯具了。
12L的算法有问题c2的值比c1少时会借位高位的g或r会少1,1好像很小可以忽视,可当值为0时一样杯具。
我还是老老实实优化楼主位好了,
楼主位的rgb值不需移位,直接掩模运算就行按rc=fc-(fc-bc)/al最后合并就成了r返回f前景色b背景al透明度1为全透明32为不透明
不是1楼是楼主位

出0入0汤圆

发表于 2011-12-24 10:17:15 | 显示全部楼层
回复【27楼】hemjidn  捱多年
不能除256,当al<8时就会杯具了。
12l的算法有问题c2的值比c1少时会借位高位的g或r会少1,1好像很小可以忽视,可当值为0时一样杯具。
我还是老老实实优化楼主位好了,
楼主位的rgb值不需移位,直接掩模运算就行按rc=fc-(fc-bc)/al最后合并就成了r返回f前景色b背景al透明度1为全透明32为不透明
不是1楼是楼主位
-----------------------------------------------------------------------

嗯...我想起来了,当初设成0~32级是我考虑过的....的确是不能除256,除256得是RGB888才行

但是LZ怀疑算法的准确性,可以自行做个比较,我这里给出我的比较用代码,vc++6.0

#include "stdafx.h"

typedef        unsigned char        u8;
typedef        unsigned short        u16;
typedef        unsigned long        u32;

#define RGB(r,g,b)  (u16)((b>>3)|(g>>2<<5)|(r>>3<<11))



u16 make_alpha16_fast(u32 c1, u32 c2, u8 al)  
{  
    c1 = (c1 | c1<<16) & 0x07e0f81f;  
    c2 = (c2 | c2<<16) & 0x07e0f81f;  
    c1 = ((c2-c1)*al/32 + c1) & 0x07e0f81f;  
    return (u16)(c1|(c1>>16));  
}

u16 make_alpha16_std(u16 c1, u16 c2, u8 al)  
{  
        u8 r1,g1,b1;
        u8 r2,g2,b2;
        r1 = (c1>>11<<3);
        g1 = (c1>>5<<2);
        b1 = (c1<<3);
        //
        r2 = (c2>>11<<3);
        g2 = (c2>>5<<2);
        b2 = (c2<<3);
        //
        r2 = (r2-r1)*al/32 + r1;
        g2 = (g2-g1)*al/32 + g1;
        b2 = (b2-b1)*al/32 + b1;
    return RGB(r2,g2,b2);  
}


int main(int argc, char* argv[])
{
        u16 c1 = RGB(0,0,0);
        u16 c2 = RGB(255,255,255);
        u8 al;
        for(al=0;al<=32;al++)
        {
                printf("al=%d, color_fast=%x\n", al, make_alpha16_fast(c1,c2,al));
                printf("al=%d, color_std =%x\n\n", al, make_alpha16_std(c1,c2,al));
        }
        return 0;
}


(原文件名:1.JPG)


(原文件名:2.JPG)

出0入0汤圆

发表于 2011-12-24 10:23:10 | 显示全部楼层
上面 弄错了,设的c2>c1了....
c2<c1时的确有些问题....但影响不大,只是个别有点小误差,显示上差别不算太大...

可以看出,alpha取值从25到31这7个值时会有问题,颜色分量上误差有时会有1....影响并不大

31a6 = 00110 001101 00110
31c6 = 00110 001110 00110

2965 = 00101 001011 00101
2985 = 00101 001100 00101

2124 = 00100 001001 00100
2144 = 00100 001010 00100

18e3 = 00011 000111 00011
1903 = 00011 001000 00011

10a2 = 00010 000101 00010
18c3 = 00011 000110 00011

861  = 00001 000011 00001
1082 = 00010 000100 00010

20   = 00000 000001 00000
841  = 00001 000010 00001


(原文件名:1.JPG)

出0入0汤圆

发表于 2011-12-24 10:54:10 | 显示全部楼层
综合28L,29L所述,我在12L提供的快速算法,并不存在杯具一说....只是个别情况略有误差,绝对不影响显示

事实上,标准算法也是有误差的,除非你用浮点来算,再加上四舍五入变成整型,但这样开销太大...

我在PC上使用浮点计算了一遍,结果见后面,
可以看出,在al=25,26,27时, 快速算法反而和浮点标准算法得出的结果一致,
在29L,标准算法得出的结果反而有了误差...
=========================================
al=24, color_fast=39e7
al=24, color_std =39e7

al=25, color_fast=31a6
al=25, color_std =31a6

al=26, color_fast=2965
al=26, color_std =2965

al=27, color_fast=2124
al=27, color_std =2124

al=28, color_fast=18e3
al=28, color_std =1903

al=29, color_fast=10a2
al=29, color_std =10c2

al=30, color_fast=861
al=30, color_std =1082

al=31, color_fast=20
al=31, color_std =841

al=32, color_fast=0
al=32, color_std=0
=========================================
附浮点算法:
u16 make_alpha16_std(u16 c1, u16 c2, u8 al)  
{  
    u8 r1,g1,b1;
    u8 r2,g2,b2;
    r1 = (c1>>11<<3);
    g1 = (c1>>5<<2);
    b1 = (c1<<3);
    //
    r2 = (c2>>11<<3);
    g2 = (c2>>5<<2);
    b2 = (c2<<3);
    //
    r2 = (u8)(r2*al/32.0f + r1*(32-al)/32.0f + 0.5f);
    g2 = (u8)(g2*al/32.0f + g1*(32-al)/32.0f + 0.5f);
    b2 = (u8)(b2*al/32.0f + b1*(32-al)/32.0f + 0.5f);
    return RGB(r2,g2,b2);  
}

出0入0汤圆

发表于 2011-12-24 14:06:24 | 显示全部楼层
为让更多人看看,我顶下....12L是目前最优算法^_^

出0入0汤圆

 楼主| 发表于 2011-12-24 15:06:29 | 显示全部楼层
回复【30楼】snoopyzz
-----------------------------------------------------------------------

回复【27楼】hemjidn 捱多年
-----------------------------------------------------------------------

感谢两位高手,学习了,这里rgb的确是的确是可以不用拆分,不过在运算中要注意是否会出现溢出或借位,不然颜色就乱了!!!!

回复【27楼】hemjidn 捱多年

楼主位的rgb值不需移位,直接掩模运算就行按rc=fc-(fc-bc)/al最后合并就成了r返回f前景色b背景al透明度1为全透明32为不透明
-----------------------------------------------------------------------

请问 hemjidn 这里是不是写错了,当al为1是是全透明,但为32的时候就不是不透明了!

出0入0汤圆

发表于 2011-12-24 15:21:23 | 显示全部楼层
回复【32楼】danielmi  俊俊
-----------------------------------------------------------------------

颜色乱不了的,你看我29,30楼的测试,最多是有点误差,但就算是拆分后再计算的标准算法,也会有舍入误差...
颜色本身是不会乱的
,因为借位只借一位...误差在每种颜色分量上,最多只有一位
,
但不移位,直接用rgb565运算的结果绝对是错的...扯蛋....

出0入0汤圆

发表于 2011-12-24 15:37:58 | 显示全部楼层
回复【33楼】snoopyzz  
-----------------------------------------------------------------------

你误会了我说r、g、b不用移位但是要分拆出来,运算后再合并
回复【32楼】 danielmi 俊俊
-----------------------------------------------------------------------
1时rc=bc就是背景色这对应就是前景完全透明
32时rc=fc就是前景色,对应就是不透明

出0入0汤圆

发表于 2011-12-24 15:41:57 | 显示全部楼层
回复【34楼】hemjidn  捱多年
回复【33楼】snoopyzz  
-----------------------------------------------------------------------
你误会了我说r、g、b不用移位但是要分拆出来,运算后再合并
回复【32楼】 danielmi 俊俊
-----------------------------------------------------------------------
1时rc=bc就是背景色这对应就是前景完全透明
32时rc=fc就是前景色,对应就是不透明
-----------------------------------------------------------------------

但你说的快速算法杯具,并不存在.....请参考29L,30L的实验数据,31L是结论

出0入0汤圆

发表于 2011-12-24 15:44:40 | 显示全部楼层
移位应该比直接用运算快的,就像右移1位比除以2要快~

出0入0汤圆

发表于 2011-12-24 16:03:45 | 显示全部楼层
免不连续删楼上
回复【35楼】snoopyzz
-----------------------------------------------------------------------
哦回去c1用纯绿色c2用纯蓝色半透试试,手机上网

出0入0汤圆

发表于 2011-12-24 21:48:18 | 显示全部楼层
121L测试通过。
除了有点误差,这点误差不是由进或借位产生的,如果是c2<c1就会杯具,这个误差应该是除法运算引入的。
c2<c1虽然有借位,但在+c2时又产生进位所以并没有发生预想的杯具。

出0入0汤圆

发表于 2011-12-24 23:40:35 | 显示全部楼层
mark.........

出0入0汤圆

发表于 2011-12-25 07:41:12 | 显示全部楼层
Mk

出0入0汤圆

发表于 2011-12-26 09:20:40 | 显示全部楼层
mark.........

出0入0汤圆

发表于 2011-12-26 09:43:27 | 显示全部楼层
回复【36楼】nongxiaoming  
移位应该比直接用运算快的,就像右移1位比除以2要快~

-----------------------------------------------------------------------

正常点的编译器,>>1和/2没区别....编译器不是傻子,懂得如何优化

出0入0汤圆

发表于 2012-5-9 13:02:14 | 显示全部楼层
mark

出0入0汤圆

发表于 2014-10-4 21:57:33 | 显示全部楼层
顶一下,
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-8-26 09:13

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表