Как бы вы усреднили два 32-битных цвета, упакованные в целое число? - PullRequest
3 голосов
/ 09 декабря 2011

Я пытаюсь усреднить два цвета.

Мое оригинальное (ужасное) орудие выглядит следующим образом:

//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-битному (альфа потеряно):

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

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

Ответы [ 2 ]

5 голосов
/ 09 декабря 2011

Попробуйте расширить маску до 32 бит, например:

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

Edit: я сделал быструю проверку, и, похоже, это работает для моего тестового случая. Хорошая формула, кстати!

4 голосов
/ 09 декабря 2011

Цель состоит в следующем:

(a + b) / 2 = ((a ^ b) >> 1) + (a & b)

И примените его ко всем четырем байтам целого числа.Если бы это был всего один байт, то сдвиг вправо на 1 бит отбросил бы самый правый бит.Однако в этом случае самый правый бит из первых 3 байтов не отбрасывается - он сдвигается в соседний байт.Следует помнить, что вам нужна маска последнего бита каждого байта, чтобы она не «загрязняла» соседний байт во время сдвига.Например, скажем, что a ^ b это:

a XOR b = 1011 1101 1110 1001

Сдвиг вправо на 1 бит без маски будетвыглядеть так:

(a XOR b) >> 1 = 0101 1110 1111 0100

Что не так.Нулевая маска выводит последний бит каждого байта, чтобы этого не произошло:

(a XOR b) И 0xfefefefe = 1010 1100 1110 1000

ТогдаВы можете безопасно сдвинуть это значение вправо:

((a XOR b) И 0xfefefefe) = 0101 0110 0111 0100

Итак:

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

Следует иметь в виду, что C не отличает арифметическое смещение вправо от логического смещения вправо с помощью своего оператора.Вы должны убедиться, что целочисленные значения, которые вы смещаете, являются беззнаковыми, чтобы предотвращали специфичное для реализации смещение целых чисел со знаком voodoo .

EDIT : я думаю, что @dasblinkenlight, возможно, избил меня этим ответом.Просто остерегайтесь сдвигать целые числа со знаком, и вы должны быть хорошими.

...