在C中将RGB转换为RGBA

Yan*_*nis 5 c image

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

Mik*_*ola 5

你想要它有多棘手?您可以将其设置为一次复制一个 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];
    }
}
Run Code Online (Sandbox Code Playgroud)

最后的额外情况是处理 rgb 数组缺少一个字节的事实。您还可以使用对齐移动和 SSE 指令使其速度更快,一次处理 4 个像素的倍数。如果你真的很有野心,你可以尝试更可怕的混淆方法,例如将缓存行预取到 FP 寄存器中,然后一次性将它传输到另一个图像。当然,您从这些优化中获得的收益将高度依赖于您所针对的特定系统配置,我真的很怀疑做任何这些而不是简单的事情是否有很多好处。

我的简单实验证实,这确实快了一点,至少在我的 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;
}
Run Code Online (Sandbox Code Playgroud)

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

图像大小 = 262144 字节

迭代次数 = 10000

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

已用时间:3.830000

平均时间:0.000383

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

经过时间:2.390000

平均时间:0.000239

因此,在美好的一天,速度可能会快 40% 左右。


kra*_*mir 0

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