快速方法复制内存并进行转换-从ARGB到BGR

66

概要

我有一个图像缓冲区,需要将其转换为另一种格式。原始图像缓冲区是四个通道,每个通道8位,包括Alpha、Red、Green和Blue。目标缓冲区是三个通道,每个通道8位,包括Blue、Green和Red。

所以暴力方法是:

// Assume a 32 x 32 pixel image
#define IMAGESIZE (32*32)

typedef struct{ UInt8 Alpha; UInt8 Red; UInt8 Green; UInt8 Blue; } ARGB;
typedef struct{ UInt8 Blue; UInt8 Green; UInt8 Red; } BGR;

ARGB orig[IMAGESIZE];
BGR  dest[IMAGESIZE];

for(x = 0; x < IMAGESIZE; x++)
{
     dest[x].Red = orig[x].Red;
     dest[x].Green = orig[x].Green;
     dest[x].Blue = orig[x].Blue;
}
然而,我需要比循环和三个字节复制提供的速度更快。我希望有一些技巧可以用来减少内存读写的次数,因为我在32位机器上运行。
附加信息
每个图像至少是4个像素的倍数。所以我们可以处理16个ARGB字节并将它们移动到每个循环的12个RGB字节中。也许这个事实可以用来加速处理,尤其是它刚好落在32位边界上。
我可以使用OpenCL——虽然这需要将整个缓冲区移动到GPU内存中,然后再将结果移回来,但OpenCL可以同时处理图像的许多部分,而且大块内存移动实际上非常高效,这可能是值得探索的。
虽然我上面给出了小缓冲区的例子,但我实际上正在移动高清视频(1920x1080)和有时更大,大多数情况下是较小的缓冲区,因此尽管32x32的情况可能微不足道,但逐字节复制8.3MB的图像数据确实非常糟糕。
在Intel处理器(Core 2及以上)上运行,因此我知道存在流式传输和数据处理命令,但我不知道它们在哪里-也许指向寻找专门的数据处理指令的指针将是有益的。
这将进入一个OS X应用程序,我正在使用XCode 4。如果汇编语言很简单并且是显而易见的方法,我很愿意走上这条路,但是在这个设置上没有这样做让我担心会浪费太多时间。
伪代码也可以-我不需要完整的解决方案,只需要算法和任何可能不明显的花招的解释。

3
编译器是否将BGR对齐到DWORD? - marinara
1
@marinara 不,它是按字节对齐的。 - Adam Davis
1
除非数据是从GPU进入系统,否则使用GPU来处理这个任务是没有意义的。你应该能够通过CPU饱和内存总线。 - Stephan Eggermont
我没有玩过别人的代码,但据我所知没有人提到了相当于for(x = 0; x < IMAGESIZE; x++) { dest[x].Red = orig[x].Red; } for(x = 0; x < IMAGESIZE; x++) { dest[x].Green = orig[x].Green; } for(x = 0; x < IMAGESIZE; x++) { dest[x].Blue = orig[x].Blue; } 的可能性。在这种情况下,简单的循环是否超过了位操作? - Mark Hurd
11个回答

3
typedef struct{ UInt8 Alpha; UInt8 Red; UInt8 Green; UInt8 Blue; } ARGB;
typedef struct{ UInt8 Blue; UInt8 Green; UInt8 Red; } BGR;

除了汇编或编译器内部函数外,我可能会尝试进行以下操作,同时非常小心地验证最终行为,因为其中一些行为(涉及联合)很可能取决于编译器实现。
union uARGB
{
   struct ARGB argb;
   UInt32 x;
};
union uBGRA
{
   struct 
   {
     BGR bgr;
     UInt8 Alpha;
   } bgra;
   UInt32 x;
};

然后针对您的代码内核,使用适当的循环展开:

inline void argb2bgr(BGR* pbgr, ARGB* pargb)
{
    uARGB* puargb = (uARGB*)pargb;
    uBGRA ubgra;
    ubgra.x = __byte_reverse_32(pargb->x);
    *pbgr = ubgra.bgra.bgr;
}

其中__byte_reverse_32()假定存在一个编译器内置函数,用于反转32位字的字节顺序。

总结底层方法:

  • 将ARGB结构视为32位整数
  • 反转32位整数
  • 将反转后的32位整数视为(BGR)A结构
  • 让编译器复制(BGR)A结构中的(BGR)部分

我已经使用低级字符串处理指令(如lodsdstosd)在汇编中实现了类似的方法。不幸的是,这种尝试似乎是无用的——提出的pshufb解决方案得分更高。 - eugene_che

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接