混合像素(半透明)的更有效方法?
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 的角度来看):
混合整数和浮点数
这需要转换,有时成本非常高...最好使用 <0..255>
范围内的整数权重 (alpha),然后除以 255 或位移位除以 8。这很可能是很多更快。
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
,方法一样...
函数开销
你得到了混合单个像素的函数。这意味着它会被调用很多。每次调用混合区域(矩形)通常比基于每个像素调用东西快得多。因为你以这种方式丢弃堆栈。要限制这一点,您可以尝试这些(对于被大量调用的函数):
重新编码您的应用程序,以便您可以混合区域而不是像素,从而减少函数调用。
通过降低被调用函数的操作数、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 混合)。
我正在为一款小型 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 的角度来看):
混合整数和浮点数
这需要转换,有时成本非常高...最好使用
<0..255>
范围内的整数权重 (alpha),然后除以 255 或位移位除以 8。这很可能是很多更快。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
,方法一样...函数开销
你得到了混合单个像素的函数。这意味着它会被调用很多。每次调用混合区域(矩形)通常比基于每个像素调用东西快得多。因为你以这种方式丢弃堆栈。要限制这一点,您可以尝试这些(对于被大量调用的函数):
重新编码您的应用程序,以便您可以混合区域而不是像素,从而减少函数调用。
通过降低被调用函数的操作数、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 混合)。