256色调色板与Alpha混合[1]

[入库:2005年8月19日] [更新:2007年3月25日]

本文简介:选择自 rockcarry 的 blog

    256色视频模式由于采用了调色板,在显存里存放的像素值实际上是是调色板的索引号,256色的bmp文件也与之类似,其数据域里存放的也是调色板的索引号,这种情况下给我们的alpha混客带来极大的不方便。

    在实际的应用中,通常是选定一个固定的调色板,然后在这个调色板下进行绘图操作。这是因为一副图像在不同的调色板下有不同的显示效果,两个采用不同调色板的位图在一般情况下是不能同屏显示的。所以通常的情况是选择一个通用的调色板,在把各个位图都抖动处理为使用这个通用调色板的位图,从而解决了同屏显示的问题。这样看来,如何选择一个通用的调色板就是关键了。

    大家都知道vga/svga的调色板寄存器通常为6 bits,每个寄存器存放一个颜色分量,一个rgb颜色向量就需要三个寄存器来存放,因此最多能表示 2^6*2^6*2^6=256k 种颜色,这个颜色范围是很大的。而vga/svga只有256组调色板寄存器,,因此要在(0,0,0)-(63,63,63)这样一个向量空间中,精选出256个颜色向量。这有点和线性代数中求向量空间的基(极大无关组)类似。我们的任务也类似于要在(0,0,0)-(63,63,63)这个颜色空间中找出一个基。简单的说就是要找出256个颜色向量,组成一个颜色调色板,并且要使这256个颜色均匀分布于(0,0,0)-(63,63,63)这个空间中,并且还要保证其独立性。详细的做法这里不再探讨,而只给出一个比较通用的调色板。
    r(i)=(i/32%8)*9;
    g(i)=(i/4%8)*9;
    b(i)=(i%4)*21;
    其中i为寄存器组号,r(i)、g(i)、b(i)分别为该寄存器的rgb颜色分量值,这是一个从i到r(i)、g(i)、b(i)的变换式。
    由此可以写出其逆变换式:
    i=r/9*32+g/9*4+b/21;

    做一下优化,可以不做乘法和除法运算,得到如下式子:
    r(i)=(((i>>5)%8)<<3)+((i>>5)%8);
    g(i)=(((i>>2)4%8)<<3)+((i>>2)4%8);
    b(i)=((i%4)<<4)+((i%4)<<2)+(i%4);

    i=((r/9)<<5)+((g/9)<<2)+b/21;

    根据这两个变换式我们可以写出两个宏,用于求取对应(r,g,b)的颜色号i,和颜色号i对应的(r,g,b)值。
    #define rgb(r,g,b)  ((((r)/9)<<5)+(((g)/9)<<2)+(b)/21)
    #define argb(r,g,b) rgb(r+4,g+4,b+10)
    #define getrgb(i,pr,pg,pb) {*(pr)=(((i>>5)%8)<<3)+((i>>5)%8);*(pg)=(((i>>2)4%8)<<3)+((i>>2)4%8);(*pb)=((i%4)<<4)+((i%4)<<2)+(i%4);}

    其中argb(adjusted rgb)宏是对rgb宏的矫正,因为rgb宏存在误差。

    这样我们就建立起了i与(r,g,b)的对应关系,这我为我们的alpha混合铺平了道路。

    现在再谈谈alpha混合。alpha混合指的是给定两个点p1、p2,其rgb颜色分量分别为(r1,g1,b1)和(r2,g2,b2),假定p1位于p2的后面,p2的透明度为a(0%<a<100%),要求我没透过点p2看到p1的颜色值是多少。假定该值为p3(r3,g3,b3),其计算公式如下:
    r3=(1-a)*r2+a*r1;
    g3=(1-a)*g2+a*g1;
    b3=(1-a)*b2+a*b1;
    这就是通常所说的alpha混合。

    优化一下得到:
    r3=r2+a*(r1-r2);
    g3=g2+a*(g1-g2);
    b3=b2+a*(b1-b2);
    少做了一次乘法运算。但由于a为浮点数,运算起来仍然很慢,所以一般不采用上面的公式,而采用整数级的alpha混合,如下:
    r2=r2+n*(r1-r2)/256;
    g2=g2+n*(g1-g2)/256;
    b2=b2+n*(b1-b2)/256;
    以上为256级alpha混合公式,由于vga/svga调色板寄存器为6bits,所以做256色的alpha混合意义不大。

    而采用一下的64级alpha混合公式:
    r2=r2+n*(r1-r2)/64;
    g2=g2+n*(g1-g2)/64;
    b2=b2+n*(b1-b2)/64;

    进一步优化为l:
    r2=r2+(n*(r1-r2)>>6);
    g2=g2+(n*(g1-g2)>>6);
    b2=b2+(n*(b1-b2)>>6);

    仅做了一次乘法运算,这样程序应该能跑得飞快了。

    下面给出混合一个点的alpha算法:
    int alpha(int p1,int p2,int n)
    {
        int c1[3];
        int c2[3];
        int c3[3];

        getrgb(p1,c1,c1+1,c1+2);
        getrgb(p2,c2,c2+1,c2+2);

        c3[0]=c2[0]+(n*(c1[0]-c2[0])>>6);
        c3[1]=c2[1]+(n*(c1[1]-c2[1])>>6);
        c3[2]=c2[2]+(n*(c1[2]-c2[2])>>6);

        return argb(c3[0],c3[1],c3[2]);
    }

    对半透明混合,可有如下更快的公式:
    r2=r2+((r1-r2)>>1);
    g2=g2+((g1-g2)>>1);
    b2=b2+((b1-b2)>>1);
    这个公式没有乘法和除法,半透明在游戏中运用也很广。

    以下是半透明的alpha混合:
    int alpha(int p1,int p2,int n)
    {
        int c1[3];
        int c2[3];
        int c3[3];

        getrgb(p1,c1,c1+1,c1+2);
        getrgb(p2,c2,c2+1,c2+2);

        c3[0]=c2[0]+((c1[0]-c2[0])>>1);
        c3[1]=c2[1]+((c1[1]-c2[1])>>1);
        c3[2]=c2[2]+((c1[2]-c2[2])>>1);

        return argb(c3[0],c3[1],c3[2]);

本文关键:256色调色板与Alpha混合
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top