我正在使用 c++ ,我想使用以下代码进行 alpha 混合。
#define CLAMPTOBYTE(color) \
if ((color) & (~255)) { \
color = (BYTE)((-(color)) >> 31); \
} else { \
color = (BYTE)(color); \
}
#define GET_BYTE(accessPixel, x, y, scanline, bpp) \
((BYTE*)((accessPixel) + (y) * (scanline) + (x) * (bpp)))
for (int y = top ; y < bottom; ++y)
{
BYTE* resultByte = GET_BYTE(resultBits, left, y, stride, bytepp);
BYTE* srcByte = GET_BYTE(srcBits, left, y, stride, bytepp);
BYTE* srcByteTop = GET_BYTE(srcBitsTop, left, y, stride, bytepp);
BYTE* maskCurrent = GET_GREY(maskSrc, left, y, width);
int alpha = 0;
int red = 0;
int green = 0;
int blue = 0;
for (int x = left; x < right; ++x)
{
alpha = *maskCurrent;
red = (srcByteTop[R] * alpha + srcByte[R] * (255 - alpha)) / 255;
green = (srcByteTop[G] * alpha + srcByte[G] * (255 - alpha)) / 255;
blue = (srcByteTop[B] * alpha + srcByte[B] * (255 - alpha)) / 255;
CLAMPTOBYTE(red);
CLAMPTOBYTE(green);
CLAMPTOBYTE(blue);
resultByte[R] = red;
resultByte[G] = green;
resultByte[B] = blue;
srcByte += bytepp;
srcByteTop += bytepp;
resultByte += bytepp;
++maskCurrent;
}
}
但是我发现它仍然很慢,合成两个 600 * 600 图像大约需要 40 - 60 毫秒。有什么方法可以将速度提高到 16ms 以下?
任何机构都可以帮助我加快这段代码的速度吗?非常感谢!
原文由 user25749 发布,翻译遵循 CC BY-SA 4.0 许可协议
使用 SSE - 从第 131 页开始。
基本工作流程
从 src 加载 4 个像素(16 个 1 字节数字) RGBA RGBA RGBA RGBA(流式加载)
再加载 4 个要与 srcbytetop RGBx RGBx RGBx RGBx 混合的内容
做一些 swizzling,使 1 中的 A 项填满每个插槽,即
xxxA xxxB xxxC xxxD -> AAAA BBBB CCCC DDDD
在下面的解决方案中,我选择重新使用现有的“maskcurrent”数组,但是将 alpha 集成到 1 的“A”字段中将需要更少的内存负载,因此速度更快。在这种情况下,Swizzling 可能是:并使用掩码选择 A、B、C、D。右移 8,或使用原始,右移 16,或再次。
将上述内容添加到每个插槽中全为 -255 的向量中
乘以 1 * 4(带有 255-alpha 的源)和 2 * 3(带有 alpha 的结果)。
您应该能够为此使用“乘以并丢弃底部 8 位”SSE2 指令。
将这两个(4和5)加在一起
将它们存放在其他地方(如果可能)或目的地顶部(如果必须)
这是您的起点:
要了解哪些 AMD 处理器将运行此代码(目前它使用 SSE2 指令),请参阅 维基百科的 AMD Turion 微处理器列表。您还可以查看 Wikipedia 上的其他处理器列表,但我的研究表明,大约 4 年前的 AMD cpu 至少都支持 SSE2。
您应该期望一个好的 SSE2 实现比您当前的代码快 8-16 倍。这是因为我们消除了循环中的分支,一次处理 4 个像素(或 12 个通道),并通过使用流指令提高缓存性能。作为 SSE 的替代方案,您可以通过消除用于饱和度的 if 检查来使现有代码运行得更快。除此之外,我还需要对您的工作负载运行分析器。
当然,最好的解决方案是使用硬件支持(即在 DirectX 中编码您的问题)并在显卡上完成。