c++ - 使用查找表更快的 alpha 混合?

标签 c++ c optimization graphics alphablending

我制作了一个查找表,允许您使用单字节 alpha channel 混合两个单字节 channel (每个 channel 256 种颜色),不使用浮点值(因此没有 float 到 int 的转换)。查找表中的每个索引对应于 channel 的 256ths 的值,与 alpha 值相关。

总而言之,要完全计算 3 channel RGB 混合,需要对每个 channel 的数组进行两次查找,再加上一次加法。这总共是 6 次查找和 3 次添加。在下面的示例中,我将颜色拆分为单独的值以便于演示。此示例说明如何通过 0 到 256 之间的 alpha 值混合三个 channel ,R G 和 B。

BYTE r1, r2, rDest;
BYTE g1, g2, gDest;
BYTE b1, b2, bDest;

BYTE av; // Alpha value
BYTE rem = 255 - av; // Remaining fraction

rDest = _lookup[r1][rem] + _lookup[r2][av];
gDest = _lookup[g1][rem] + _lookup[g2][av];
bDest = _lookup[b1][rem] + _lookup[b2][av];

效果很好。尽可能精确地使用 256 个颜色 channel 。事实上,您将使用实际的浮点计算得到相同的精确值。查找表是使用 double 开始计算的。查找表太大,不适合这篇文章(65536 字节)。 (如果你想要它的拷贝,请发送电子邮件至 ten.turtle.toes@gmail.com,但不要指望明天之前回复,因为我现在要 sleep 了。)

那么……你怎么看?值不值?

最佳答案

我有兴趣查看一些基准。

有一种算法可以在没有任何浮点计算或查找表的情况下进行完美的 alpha 混合。您可以在以下 document 中找到更多信息(算法和代码在最后说明)

我很久以前也做了一个 SSE 实现,如果你有兴趣的话......

void PreOver_SSE2(void* dest, const void* source1, const void* source2, size_t size)
{
    static const size_t STRIDE = sizeof(__m128i)*4;
    static const u32 PSD = 64;

    static const __m128i round = _mm_set1_epi16(128);
    static const __m128i lomask = _mm_set1_epi32(0x00FF00FF);

    assert(source1 != NULL && source2 != NULL && dest != NULL);
    assert(size % STRIDE == 0);

    const __m128i* source128_1 = reinterpret_cast<const __m128i*>(source1);
    const __m128i* source128_2 = reinterpret_cast<const __m128i*>(source2);
    __m128i*       dest128 = reinterpret_cast<__m128i*>(dest);  

    __m128i d, s, a, rb, ag, t;

    for(size_t k = 0, length = size/STRIDE; k < length; ++k)    
    {
        // TODO: put prefetch between calculations?(R.N)
        _mm_prefetch(reinterpret_cast<const s8*>(source128_1+PSD), _MM_HINT_NTA);
        _mm_prefetch(reinterpret_cast<const s8*>(source128_2+PSD), _MM_HINT_NTA);   

        // work on entire cacheline before next prefetch
        for(int n = 0; n < 4; ++n, ++dest128, ++source128_1, ++source128_2)
        {
            // TODO: assembly optimization use PSHUFD on moves before calculations, lower latency than MOVDQA (R.N) http://software.intel.com/en-us/articles/fast-simd-integer-move-for-the-intel-pentiumr-4-processor/

            // TODO: load entire cacheline at the same time? are there enough registers? 32 bit mode (special compile for 64bit?) (R.N)
            s = _mm_load_si128(source128_1);        // AABGGRR
            d = _mm_load_si128(source128_2);        // AABGGRR

            // PRELERP(S, D) = S+D - ((S*D[A]+0x80)>>8)+(S*D[A]+0x80))>>8
            // T = S*D[A]+0x80 => PRELERP(S,D) = S+D - ((T>>8)+T)>>8

            // set alpha to lo16 from dest_
            a = _mm_srli_epi32(d, 24);          // 000000AA 
            rb = _mm_slli_epi32(a, 16);         // 00AA0000
            a = _mm_or_si128(rb, a);            // 00AA00AA

            rb = _mm_and_si128(lomask, s);      // 00BB00RR     
            rb = _mm_mullo_epi16(rb, a);        // BBBBRRRR 
            rb = _mm_add_epi16(rb, round);      // BBBBRRRR
            t = _mm_srli_epi16(rb, 8);          
            t = _mm_add_epi16(t, rb);
            rb = _mm_srli_epi16(t, 8);          // 00BB00RR 

            ag = _mm_srli_epi16(s, 8);          // 00AA00GG     
            ag = _mm_mullo_epi16(ag, a);        // AAAAGGGG     
            ag = _mm_add_epi16(ag, round);
            t = _mm_srli_epi16(ag, 8);
            t = _mm_add_epi16(t, ag);
            ag = _mm_andnot_si128(lomask, t);   // AA00GG00     

            rb = _mm_or_si128(rb, ag);          // AABGGRR      pack

            rb = _mm_sub_epi8(s, rb);           // sub S-[(D[A]*S)/255]
            d = _mm_add_epi8(d, rb);            // add D+[S-(D[A]*S)/255]

            _mm_stream_si128(dest128, d);
        }
    }   
    _mm_mfence();   //ensure last WC buffers get flushed to memory      
}

关于c++ - 使用查找表更快的 alpha 混合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5438313/

相关文章:

c++ - Openmp 和减少 std::vector?

c++ - 可视化每个线程正在运行的功能的工具

c++ - 安卓工作室 + NDK : C++ Editor does not work

php - 如何动态压缩 JS 或 CSS

java - 通过使用单独的对象帮助 JVM 进行堆栈分配

c++ - 为什么 main 不能是 constexpr?

c - 如何在子进程中使用 --leak-check=full 运行 valgrind?

c - 如何在 getchar() 循环中检测 C 字符串中的最后一个字符

c - C (C99) 中嵌套函数调用的限制

ruby - 常量与按需实例化的对象——哪个最优?