混合像素(半透明)的更有效方法?

More efficient way to blend pixels (semi-transparency)?

我正在为一款小型 2d 游戏在其他图像之上绘制半透明图像。目前要混合我使用此处找到的公式的图像:https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending

我的实现如下;

private static int blend(int source, int dest, int trans)
{
    double alpha = ((double) trans / 255.0);
    int sourceRed = (source >> 16 & 0xff);
    int sourceGreen = (source >> 8 & 0xff);
    int sourceBlue = (source & 0xff);
    int destRed = (dest >> 16 & 0xff);
    int destGreen = (dest >> 8 & 0xff);
    int destBlue = (dest & 0xff);

    int blendedRed = (int) (alpha * sourceRed + (1.0 - alpha) * destRed);
    int blendedGreen = (int) (alpha * sourceGreen + (1.0 - alpha) * destGreen);
    int blendedBlue = (int) (alpha * sourceBlue + (1.0 - alpha) * destBlue);

    return (blendedRed << 16) + (blendedGreen << 8) + blendedBlue;
}

现在,它工作正常,但它有相当高的开销,因为它在每一帧的每个像素上都被调用。我的性能下降了大约 30% FPS,而不是简单地渲染图像而不混合。

我只是想知道是否有人能想出更好的方法来优化这段代码,因为我可能做了太多的位操作。

不是 java 编码员(所以带着偏见阅读)但你做的一些事情真的错了(从我的 C++ 和低级 gfx 的角度来看):

  1. 混合整数和浮点数

    这需要转换,有时成本非常高...最好使用 <0..255> 范围内的整数权重 (alpha),然后除以 255 或位移位除以 8。这很可能是很多更快。

  2. bitshifting/masking获取字节

    是的,它很好,但有更简单、更快速的方法,只需使用

    enum{
        _b=0,   // db
        _g=1,
        _r=2,
        _a=3,
        };
    
    union color
        {
        DWORD dd;    // 1x32 bit unsigned int
        BYTE db[4];  // 4x8 bit unsigned int
        };
    
    color col;
    col.dd=some_rgba_color;
    r = col.dd[_r]; // get red channel
    col.dd[_b]=5;   // set blue channel
    

    体面的编译器可以在内部自行优化代码的某些部分,但我怀疑它是否可以在任何地方做到这一点...

    你也可以用指针代替union,方法一样...

  3. 函数开销

    你得到了混合单个像素的函数。这意味着它会被调用很多。每次调用混合区域(矩形)通常比基于每个像素调用东西快得多。因为你以这种方式丢弃堆栈。要限制这一点,您可以尝试这些(对于被大量调用的函数):

    重新编码您的应用程序,以便您可以混合区域而不是像素,从而减少函数调用。

    通过降低被调用函数的操作数、return 值和内部变量来减少堆栈垃圾,以限制每次调用 allocated/freed/overwritten/copied 的 RAM 量...例如通过使用静态或全局变量例如,Alpha 很可能不会有太大变化。或者您可以直接使用在颜色中编码的 alpha,而不是将 alpha 作为操作数。

    使用 inline#define 之类的宏将源代码直接放入代码而不是函数调用。

对于初学者,我会尝试将您的函数体重新编码为如下内容:

enum{
    _b=0,   // db
    _g=1,
    _r=2,
    _a=3,
    };

union color
    {
    unsigned int dd;    // 1x32 bit unsigned int
    unsigned char db[4];  // 4x8 bit unsigned int
    };

private static unsigned int blend(unsigned int src, unsigned int dst, unsigned int alpha)
    {
    unsigned int i,a,_alpha=255-alpha;
    color s,d;
    s.dd=src;
    d.dd=dst;
    for (i=0;i<3;i++)
        { 
        a=(((unsigned int)(s.db[i]))*alpha) + (((unsigned int)(d.db[i]))*_alpha);
        a>>=8;
        d.db[i]=a;
        }
    return d.dd;
    }

但是,如果您想要真正的速度,请使用 GPU(OpenGL 混合)。