Конвертировать RGB в RGBA в C - PullRequest
       1

Конвертировать RGB в RGBA в C

5 голосов
/ 15 августа 2011

Мне нужно скопировать содержимое байтового массива, представляющего изображение в байтовом порядке RGB, в другой буфер RGBA (4 байта на пиксель). Альфа-канал будет заполнен позже. Какой самый быстрый способ достичь этого?

Ответы [ 5 ]

4 голосов
/ 15 августа 2011

Насколько это сложно?Вы можете настроить его так, чтобы копировать 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% быстрее в хороший день.

4 голосов
/ 15 августа 2011

Быстрее всего было бы использовать библиотеку, которая реализует преобразование для вас, а не писать ее самостоятельно.На какую платформу вы нацеливаетесь?

Если вы по какой-то причине настаиваете на том, чтобы написать ее самостоятельно, сначала напишите простую и правильную версию.Используйте это.Если производительность недостаточна, , тогда , вы можете подумать об ее оптимизации.В общем, этот вид преобразования лучше всего выполнять с использованием векторных перестановок, но точная оптимальная последовательность варьируется в зависимости от целевой архитектуры.

2 голосов
/ 15 августа 2011
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];
    }
}
1 голос
/ 15 августа 2011

Я думаю, что я напоминаю учебник 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
    }
}

То, что он делает, говорит само за себя, и должно быть легко преобразовать это в добавление альфа-байта.

1 голос
/ 15 августа 2011

Просто создайте массив размером 4/3 исходного массива. Прочитать весь массив и записать его в массив RGBA, но через каждые 3 байта вставьте 255 для альфа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...