你如何平均将两个32位颜色打包成一个整数?

时间:2011-12-09 03:16:56

标签: colors int average

我正试图平均两种颜色。

我原来的(可怕的)工具如下:

//color is a union
int ColorAverage(int c1, int c2) {
    color C1(c1);
    color C2(c2);
    return color(
        (unsigned char)(0.5f * C1.a + 0.5f * C2.a),
        (unsigned char)(0.5f * C1.r + 0.5f * C2.r),
        (unsigned char)(0.5f * C1.g + 0.5f * C2.g),
        (unsigned char)(0.5f * C1.b + 0.5f * C2.b)
    ).c;
}

我目前的解决方案如下(表现更好):

int ColorAverage(int c1, int c2) {
    unsigned char* b1 = reinterpret_cast<unsigned char*>(&c1);
    unsigned char* b2 = reinterpret_cast<unsigned char*>(&c2);
    int value;
    unsigned char* bv = reinterpret_cast<unsigned char*>(&value);
    bv[0] = (b1[0] + b2[0]) / 2;
    bv[1] = (b1[1] + b2[1]) / 2;
    bv[2] = (b1[2] + b2[2]) / 2;
    bv[3] = (b1[3] + b2[3]) / 2;
    return(value);
}

然而,它仍然很慢(大约占我帧时间的3%)。

我确实找到了24位的解决方案,但它不适用于32位(alpha丢失):

#define AVERAGE(a, b)   ( ((((a) ^ (b)) & 0xfffefefeL) >> 1) + ((a) & (b)) )

http://www.compuphase.com/graphic/scale3.htm#HSIEH1

2 个答案:

答案 0 :(得分:5)

尝试将掩码扩展到32位,如下所示:

#define AVERAGE(a, b)   ( ((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b)) )

编辑:我做了一个快速检查,它似乎适用于我的测试用例。顺便说一下好的配方!

答案 1 :(得分:4)

目标是采取以下行动:

  

(a + b)/ 2 =((a ^ b)&gt;&gt; 1)+(a&amp; b)

并将其应用于整数的所有四个字节。如果这只是一个字节,则右移1位会丢弃最右边的位。但是,在这种情况下,前3个字节的最右边位不会被丢弃---它会移入相邻字节。要记住的想法是,您需要屏蔽每个字节的最后一位,以便在移位期间不会“污染”相邻字节。例如,假设a ^ b是这样的:

  

a XOR b = 1011 1101 1110 1001

没有掩码的1位右移将如下所示:

  

(a XOR b)&gt;&gt; 1 = 0101 1110 1111 0100

哪个错了。掩码零超出每个字节的最后一位,因此不会发生这种情况:

  

(一个XOR b)和0xfefefefe = 1010 1100 1110 1000

然后你可以安全地将这个值转移到右边:

  

((XOR b)AND 0xfefefefe)= 0101 0110 0111 0100

所以:

#define AVERAGE(a, b)   ( ((((a) ^ (b)) & 0xfefefefeL) >> 1) + ((a) & (b)) )

要记住的一点是,C不会将算术右移与逻辑右移与运算符区分开来。您需要确保要转移的整数未签名,为prevent implementation-specific signed-integer shift voodoo

编辑:我认为@dasblinkenlight可能已经击败了我这个答案。只要小心改变有符号整数,你应该很好。