7

我正在尝试将 rgba 缓冲区转换为 argb,有什么方法可以改进下一个算法,或者有其他更快的方法来执行此类操作吗?考虑到 alpha 值在 argb 缓冲区中一次并不重要,并且应该始终以 0xFF 结束。

int y, x, pixel;

for (y = 0; y < height; y++)
{
    for (x = 0; x < width; x++)
    {
     pixel = rgbaBuffer[y * width + x];
     argbBuffer[(height - y - 1) * width + x] = (pixel & 0xff00ff00) | ((pixel << 16) & 0x00ff0000) | ((pixel >> 16) & 0xff);
    }
}
4

6 回答 6

7

我将只关注交换功能:

typedef unsigned int Color32;

inline Color32 Color32Reverse(Color32 x)
{

    return
    // Source is in format: 0xAARRGGBB
        ((x & 0xFF000000) >> 24) | //______AA
        ((x & 0x00FF0000) >>  8) | //____RR__
        ((x & 0x0000FF00) <<  8) | //__GG____
        ((x & 0x000000FF) << 24);  //BB______
    // Return value is in format:  0xBBGGRRAA
}
于 2013-06-13T19:47:43.930 回答
1

假设代码没有错误(只是效率低下),我猜你想要做的就是每秒交换一次(偶数)字节(当然还有反转缓冲区),不是吗?

因此,您可以通过以下方式实现一些优化:

  • 避免移位和屏蔽操作
  • 优化循环,例如节省指数计算

我将重写代码如下:

int y, x;

for (y = 0; y < height; y++)
{
    unsigned char *pRGBA= (unsigned char *)(rgbaBuffer+y*width);
    unsigned char *pARGB= (unsigned char *)(argbBuffer+(height-y-1)*width);
    for (x = 4*(width-1); x>=0; x-=4)
    {
        pARGB[x  ]   = pRGBA[x+2];
        pARGB[x+1]   = pRGBA[x+1];
        pARGB[x+2]   = pRGBA[x  ];
        pARGB[x+3]   = 0xFF;
    }
}

请注意,更复杂的索引计算仅在外循环中执行。对于每个像素,rgbaBuffer 和 argbBuffer 都有四个访问权限,但我认为这不仅仅是通过避免按位运算和索引计算来实现的。另一种方法是(如在您的代码中)一次获取/存储一个像素(int),并在本地进行处理(这可以节省内存访问),但除非您有一些有效的方法来交换两个字节并设置本地 alpha (例如一些内联汇编,以便确保所有内容都在寄存器级别执行),它不会真正帮助。

于 2012-06-29T13:25:37.250 回答
1

您提供的代码非常奇怪,因为它洗牌的不是 rgba->argb,而是 rgba->rabg 的颜色分量。

我已经制作了这个例程的正确和优化版本。

int pixel;
int size = width * height;

for (unsigned int * rgba_ptr = rgbaBuffer, * argb_ptr = argbBuffer + size - 1; argb_ptr >= argbBuffer; rgba_ptr++, argb_ptr--)
{
    // *argb_ptr = *rgba_ptr >> 8 | 0xff000000;  // - this version doesn't change endianess
    *argb_ptr = __builtin_bswap32(*rgba_ptr) >> 8 | 0xff000000;  // This does
}

我做的第一件事是简化你的洗牌表达。很明显,XRGB 只是 RGBA >> 8。我还删除了每次迭代时数组索引的计算,并使用指针作为循环变量。这个版本比我机器上的原始版本快大约 2 倍。

如果此代码适用于 x86 CPU,您还可以使用 SSE 进行混洗。

于 2012-06-29T13:52:23.967 回答
1

我已经很晚了。但是在动态生成视频时我遇到了完全相同的问题。通过重用缓冲区,我可以只为每一帧设置 R、G、B 值并且只设置 A 一次。

见下面的代码:

byte[] _workingBuffer = null;
byte[] GetProcessedPixelData(SKBitmap bitmap)
{
    ReadOnlySpan<byte> sourceSpan = bitmap.GetPixelSpan();

    if (_workingBuffer == null || _workingBuffer.Length != bitmap.ByteCount)
    {
        // Alloc buffer
        _workingBuffer = new byte[sourceSpan.Length];

        // Set all the alpha
        for (int i = 0; i < sourceSpan.Length; i += 4) _workingBuffer[i] = byte.MaxValue;
    }

    Stopwatch w = Stopwatch.StartNew();
    for (int i = 0; i < sourceSpan.Length; i += 4)
    {
        // A
        // Dont set alpha here. The alpha is already set in the buffer
        //_workingBuffer[i] = byte.MaxValue;
        //_workingBuffer[i] = sourceSpan[i + 3];

        // R
        _workingBuffer[i + 1] = sourceSpan[i];

        // G
        _workingBuffer[i + 2] = sourceSpan[i + 1];

        // B
        _workingBuffer[i + 3] = sourceSpan[i + 2];
    }
    Debug.Print("Copied " + sourceSpan.Length + " in " + w.Elapsed.TotalMilliseconds);

    return _workingBuffer;
}

这让我在 iPhone 上的 (1920 * 1080 * 4) 缓冲区大约需要 15 毫秒,约为 8mb。

这对我来说还远远不够。我的最终解决方案是执行偏移内存复制(C# 中的 Buffer.BlockCopy),因为 alpha 并不重要。

    byte[] _workingBuffer = null;
    byte[] GetProcessedPixelData(SKBitmap bitmap)
    {
        ReadOnlySpan<byte> sourceSpan = bitmap.GetPixelSpan();
        byte[] sourceArray = sourceSpan.ToArray();

        if (_workingBuffer == null || _workingBuffer.Length != bitmap.ByteCount)
        {
            // Alloc buffer
            _workingBuffer = new byte[sourceSpan.Length];

            // Set first byte. This is the alpha component of the first pixel
            _workingBuffer[0] = byte.MaxValue;
        }

        // Converts RGBA to ARGB in ~2 ms instead of ~15 ms
        // 
        // Copies the whole buffer with a offset of 1
        //                                      R   G   B   A   R   G   B   A   R   G   B   A
        // Originally the source buffer has:    R1, G1, B1, A1, R2, G2, B2, A2, R3, G3, B3, A3
        //                                   A  R   G   B   A   R   G   B   A   R   G   B   A
        // After the copy it looks like:     0, R1, G1, B1, A1, R2, G2, B2, A2, R3, G3, B3, A3
        // So essentially we get the wrong alpha for every pixel. But all alphas should be 255 anyways.
        // The first byte is set in the alloc
        Buffer.BlockCopy(sourceArray, 0, _workingBuffer, 1, sourceSpan.Length - 1);

        // Below is an inefficient method of converting RGBA to ARGB. Takes ~15 ms on iPhone 12 Pro Max for a 8mb buffer (1920 * 1080 * 4 bytes)
        /*
        for (int i = 0; i < sourceSpan.Length; i += 4)
        {
            // A
            // Dont set alpha here. The alpha is already set in the buffer
            //_workingBuffer[i] = byte.MaxValue;
            //_workingBuffer[i] = sourceSpan[i + 3];

            byte sR = sourceSpan[i];
            byte sG = sourceSpan[i + 1];
            byte sB = sourceSpan[i + 2];

            if (sR == 0 && sG == byte.MaxValue && sB == 0)
                continue;

            // R
            _workingBuffer[i + 1] = sR;

            // G
            _workingBuffer[i + 2] = sG;

            // B
            _workingBuffer[i + 3] = sB;
        }
        */

        return _workingBuffer;
    }

代码注释了它是如何工作的。在我的同一部 iPhone 上,大约需要 2 毫秒,这对于我的用例来说已经足够了。

于 2021-02-16T00:33:08.667 回答
0

使用汇编,以下是针对英特尔的。

此示例交换红色和蓝色。

void* b = pixels;
UINT len = textureWidth*textureHeight;

__asm                                                       
{
    mov ecx, len                // Set loop counter to pixels memory block size
    mov ebx, b                  // Set ebx to pixels pointer
    label:                      
        mov al,[ebx+0]          // Load Red to al
        mov ah,[ebx+2]          // Load Blue to ah
        mov [ebx+0],ah          // Swap Red
        mov [ebx+2],al          // Swap Blue
        add ebx,4               // Move by 4 bytes to next pixel
        dec ecx                 // Decrease loop counter
        jnz label               // If not zero jump to label
}
于 2016-06-04T06:52:32.167 回答
0

(pixel << 24) | (pixel >> 8)将 32 位整数向右旋转 8 位,这会将 32 位 RGBA 值转换为 ARGB。这有效,因为:

  • pixel << 24丢弃左边的部分,RGB导致.RGBAA000
  • pixel >> 8丢弃右侧的部分,A导致.RGBA0RGB
  • A000 | 0RGB == ARGB.
于 2022-01-27T12:33:55.293 回答