[英]Convert RGB to RGBA in C
我需要以 RGB 字節順序將表示圖像的字節數組的內容復制到另一個 RGBA(每像素 4 字節)緩沖區中。 稍后將填充 Alpha 通道。 實現這一目標的最快方法是什么?
你想要它有多棘手? 您可以將其設置為一次復制一個 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 寄存器中,然后一次性將其傳送到另一個圖像。 當然,您從這些優化中獲得的里程將在很大程度上取決於您所針對的特定系統配置,我真的懷疑做任何這些而不是簡單的事情是否有很多好處。
而我的簡單實驗證實,這確實快了一點,至少在我的 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%。
最快的方法是使用為您實現轉換的庫,而不是自己編寫。 您的目標是哪個平台?
如果你因為某種原因堅持自己寫,先寫一個簡單正確的版本。 用那個。 如果性能不夠,那么可以考慮優化一下。 一般來說,這種轉換最好使用向量置換來完成,但確切的最佳序列會因目標架構而異。
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];
}
}
我想我記得一個 Nehe 教程做類似的事情,但很快。
它在這里
有趣的部分在這里:
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
}
}
它的作用是不言自明的,應該很容易將其轉換為添加 alpha 字節。
只需創建大小為源數組 4/3 的數組。 讀取整個數組並將其寫入 RGBA 數組,但在每 3 個字節后插入 255 個 alpha。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.