在C中将RGB转换为RGBA

时间:2011-08-15 18:38:08

标签: c image

我需要将表示RGB字节顺序的图像的字节数组的内容复制到另一个RGBA(每像素4个字节)缓冲区中。 Alpha通道将在稍后填充。实现这一目标的最快方法是什么?

5 个答案:

答案 0 :(得分:4)

最快的是使用一个为您实现转换的库,而不是自己编写。您要定位哪个平台?

如果您出于某种原因坚持自己编写,请先写一个简单正确的版本。用那个。如果性能不足,那么你可以考虑优化它。通常,这种转换最好使用向量置换进行,但确切的最佳序列取决于目标架构。

答案 1 :(得分:4)

你想要它有多棘手?您可以将其设置为一次复制一个4字节的字,在某些32位系统上可能会快一点:

void fast_unpack(char* rgba, const char* rgb, const int count) {
    if(count==0)
        return;
    for(int i=count; --i; rgba+=4, rgb+=3) {
        *(uint32_t*)(void*)rgba = *(const uint32_t*)(const void*)rgb;
    }
    for(int j=0; j<3; ++j) {
        rgba[j] = rgb[j];
    }
}

最后的额外情况是处理rgb数组缺少一个字节的事实。您还可以使用对齐的移动和SSE指令使其快一点,一次以4个像素的倍数工作。如果你真的有野心勃勃,你可以尝试更加可怕的模糊处理的事情,例如将缓存行预取到FP寄存器中,然后将其一次性地翻转到另一个图像。当然,你从这些优化中获得的里程数将高度依赖于你所针对的特定系统配置,而且我真的很怀疑这样做有什么好处而不是简单的事情。< / p>

我的简单实验证实,这确实比更快,至少在我的x86机器上。这是一个基准:

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>

void fast_unpack(char* rgba, const char* rgb, const int count) {
    if(count==0)
        return;
    for(int i=count; --i; rgba+=4, rgb+=3) {
        *(uint32_t*)(void*)rgba = *(const uint32_t*)(const void*)rgb;
    }
    for(int j=0; j<3; ++j) {
        rgba[j] = rgb[j];
    }
}

void simple_unpack(char* rgba, const char* rgb, const int count) {
    for(int i=0; i<count; ++i) {
        for(int j=0; j<3; ++j) {
            rgba[j] = rgb[j];
        }
        rgba += 4;
        rgb  += 3;
    }
}

int main() {
    const int count = 512*512;
    const int N = 10000;

    char* src = (char*)malloc(count * 3);
    char* dst = (char*)malloc(count * 4);

    clock_t c0, c1;    
    double t;
    printf("Image size = %d bytes\n", count);
    printf("Number of iterations = %d\n", N);

    printf("Testing simple unpack....");
    c0 = clock();
    for(int i=0; i<N; ++i) {
        simple_unpack(dst, src, count);
    }
    c1 = clock();
    printf("Done\n");
    t = (double)(c1 - c0) / (double)CLOCKS_PER_SEC;
    printf("Elapsed time: %lf\nAverage time: %lf\n", t, t/N);


    printf("Testing tricky unpack....");
    c0 = clock();
    for(int i=0; i<N; ++i) {
        fast_unpack(dst, src, count);
    }
    c1 = clock();
    printf("Done\n");
    t = (double)(c1 - c0) / (double)CLOCKS_PER_SEC;
    printf("Elapsed time: %lf\nAverage time: %lf\n", t, t/N);

    return 0;
}

以下是结果(使用g ++ -O3编译):

  

图片大小= 262144字节

     

迭代次数= 10000

     

测试简单解压....完成

     

经过时间:3.830000

     

平均时间:0.000383

     

测试棘手的解压....完成

     

经过时间:2.390000

     

平均时间:0.000239

所以,在美好的一天,也许快40%左右。

答案 2 :(得分:2)

struct rgb {
   char r;
   char g;
   char b;
};

struct rgba {
   char r;
   char g;
   char b;
   char a;
}

void convert(struct rgba * dst, const struct rgb * src, size_t num)
{
    size_t i;
    for (i=0; i<num; i++) {
        dst[i].r = src[i].r;
        dst[i].g = src[i].g;
        dst[i].b = src[i].b;
    }
}

这将是更清晰的解决方案,但是当你提到一个字节数组时,你应该使用它:

// num is still the size in pixels. So dst should have space for 4*num bytes,
// while src is supposed to be of length 3*num.
void convert(char * dst, const char * src, size_t num)
{
    size_t i;
    for (i=0; i<num; i++) {
        dst[4*i] = src[3*i];
        dst[4*i+1] = src[3*i+1];
        dst[4*i+2] = src[3*i+2];
    }
}

答案 3 :(得分:1)

只需创建大小为源阵列4/3的数组。读取整个数组并将其写入RGBA数组,但在每3字节后插入255表示alpha。

答案 4 :(得分:1)

我想我会记得一个关于做类似事情的Nehe教程,但很快。

here

有趣的部分在这里:

void flipIt(void* buffer)                       // Flips The Red And Blue Bytes (256x256)
{
    void* b = buffer;                       // Pointer To The Buffer
    __asm                               // Assembler Code To Follow
    {
        mov ecx, 256*256                    // Set Up A Counter (Dimensions Of Memory Block)
        mov ebx, b                      // Points ebx To Our Data (b)
        label:                          // Label Used For Looping
            mov al,[ebx+0]                  // Loads Value At ebx Into al
            mov ah,[ebx+2]                  // Loads Value At ebx+2 Into ah
            mov [ebx+2],al                  // Stores Value In al At ebx+2
            mov [ebx+0],ah                  // Stores Value In ah At ebx

            add ebx,3                   // Moves Through The Data By 3 Bytes
            dec ecx                     // Decreases Our Loop Counter
            jnz label                   // If Not Zero Jump Back To Label
    }
}

它的作用非常自我解释,应该很容易将其转换为添加字母字节。