Encontrei três algoritmos de conversão de RGB888 para RGB565, mas não tenho certeza de qual é o mais correto. Os dois primeiros produzem os mesmos resultados. O terceiro produz um resultado diferente, mas concorda com uma calculadora online. E o terceiro faz sentido, já que faz escala. Mas os dois primeiros serão mais rápidos, já que não há multiplicação ou divisão de inteiros.
uint8_t r8, g8, b8;
uint16_t r5, g6, b5, val;
r8 = someredvalue;
g8 = somegreenvalue;
b8 = somebluevalue;
// first method:
b5 = (b8 >> 3) & 0x001F;
g6 = ((g8 >> 2) << 5) & 0x07E0;
r5 = ((r8 >> 3) << 11) & 0xF800;
val = (r5 | g6 | b5);
printf("%d, %d, %d, %u\n", r8, g8, b8, val);
// second method:
val = ((r8 & 0b11111000) << 8) | ((g8 & 0b11111100) << 3) | (b8 >> 3);
printf("%d, %d, %d, %u\n", r8, g8, b8, val);
// third method:
r5 = r8 * 31 / 255;
g6 = g8 * 63 / 255;
b5 = b8 * 31 / 255;
val = (r5 << 11) | (g6 << 5) | (b5);
printf("%d, %d, %d, %u\n", r8, g8, b8, val);
Porque são equivalentes, pelo menos para compiladores que suportam
0bxxx
constantes binárias no estilo , que é uma extensão do C padrão. Ambos tomam os valores de cor do formato 565 como o número correto dos bits mais significativos do valor de cor correspondente no formato 888.Todos eles fazem escala. O terceiro apenas usa fatores de escala ligeiramente diferentes dos outros.
Considere o canal vermelho. Cinco bits podem representar valores de 0 a 31. Oito bits podem representar valores de 0 a 255. Matematicamente, o fator para escala linear do último intervalo para o primeiro é 31/255, como a terceira abordagem usa. Os outros dois estão dividindo por 8 (via deslocamento), que é o mesmo aritmeticamente que escalar por 32/256. Esses não são o mesmo fator de escala, é claro, mas são próximos.
Mas antes de considerar o método 3 como mais correto, você deve considerar os efeitos da aritmética de inteiros. Por exemplo, quais valores vermelhos de 8 bits mapeiam para 31 na representação de 5 bits? Com o método 3, apenas um o faz: 255. O valor 254 é dimensionado para um pouco menos que 31, que é truncado para 30. Essa variação na conversão é, portanto, não uniforme em relação a quantas cores RGB888 correspondem a cada cor RGB565.
A abordagem de bit-shifting, por outro lado, é uniforme nesse sentido. Ao converter dessa forma, cada cor RGB565 corresponde ao mesmo número (256) de cores RGB888.
No geral, os dois redimensionamentos produzem resultados muito semelhantes, dentro de 1 unidade por canal um do outro. Ambos são lineares, até o truncamento para inteiro. Nenhum é mais correto do que o outro em um sentido absoluto. As alternativas de deslocamento de bits são diferenciadas pela distribuição uniforme dos resultados no espaço RGB565. A outra alternativa é diferenciada pelo mapeamento apenas das intensidades máximas RGB888 para as intensidades máximas RGB565. Também pode haver uma diferença de desempenho, mas se isso for importante para você, então você deve medir. Para a maioria dos propósitos, eu estaria inclinado a escolher a uniformidade de uma das abordagens de deslocamento de bits, mas você pode pensar diferente.
Hmm... Eu estava executando os testes em um PC x86 mais antigo (não tenho um RPi). Ainda assim...
gcc
)m1
é mais rápido comclang
. Mas, isso é porque ele usou instruções SIMD x86 não enroladas em loop.Para que estejamos em um ponto em comum (só por diversão), anexei meu programa de benchmark completo para seus algoritmos.
Aqui está o programa de teste (
all.c
):Aqui está o que
test.c
ele precisa (deve ser um arquivo separado):Compilado com
gcc
8.3.1:A saída é:
Compilado com
clang
7.0.1:A saída: