我找到了三种 RGB888 到 RGB565 的转换算法,但我不确定哪种最正确。前两种产生相同的结果。第三种产生不同的结果,但与在线计算器一致。第三种是有道理的,因为它可以缩放。但前两种会更快,因为没有整数乘法或除法。
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);
因为它们是等效的,至少对于支持
0bxxx
-style 二进制常量的编译器而言,这是对标准 C 的扩展。两者都将 565 格式的颜色值作为 888 格式中对应颜色值的最高有效位的正确数字。它们都进行缩放。第三个只是使用与其他函数略有不同的缩放因子。
考虑红色通道。五位可以表示从 0 到 31 的值。八位可以表示从 0 到 255 的值。从数学上讲,从后者范围线性缩放到前者的因子是 31/255,正如第三种方法所使用的那样。另外两种方法是除以 8(通过移位),这在算术上与除以 32/256 相同。当然,它们不是相同的缩放因子,但它们很接近。
但在你认为方法 3 更正确之前,你应该考虑整数运算的影响。例如,哪些 8 位红色值映射到 5 位表示中的 31?使用方法 3,只有一个可以:255。值 254 缩放到略小于 31,被截断为 30。因此,转换的这种变化对于每个 RGB565 颜色对应的 RGB888 颜色数量是不一致的。
另一方面,位移方法在这方面是统一的。当以这种方式转换时,每个 RGB565 颜色对应于相同数量(256)的 RGB888 颜色。
总体而言,这两种重新缩放方法产生的结果非常相似,每个通道之间的差异在 1 个单位以内。两者都是线性的,直到截断为整数。从绝对意义上讲,没有哪个比另一个更正确。位移替代方案的特点是将结果均匀分布在 RGB565 空间上。另一种替代方案的特点是仅将最大 RGB888 强度映射到最大 RGB565 强度。性能可能也存在差异,但如果这对您很重要,那么您应该进行测量。对于大多数目的,我倾向于选择其中一种位移方法的一致性,但您可能会有不同的想法。
嗯...我在一台较旧的 x86 PC 上运行测试(没有 RPi)。不过...
gcc
)m1
使用速度更快clang
。但这是因为它使用了循环展开的 x86 SIMD 指令。为了让我们达成共识(只是为了好玩),我附上了针对您的算法的完整基准测试程序。
以下是测试程序(
all.c
):这是
test.c
它所需要的(必须是单独的文件):使用 8.3.1 编译
gcc
:输出为:
使用 7.0.1 编译
clang
:输出: