我找到了一种替代方法,它比 biziclop 的近似值快约 25%。这也是一个近似值,因为它将 alpha 的级别从 0-255 降低到 0-31(32 个 alpha 级别),但据我所知,它不会截断颜色位。
在我的 TFT 显示器上,结果看起来与 biziclop 算法的结果相同,但我没有检查单个像素值以查看差异是什么(如果有的话)。
请注意,虽然 fg 和 bg 参数是 32 位无符号的,但实际上您必须只传入 16 位 RGB565 颜色。算法要求函数内的 32 位宽度。
/**
* Fast RGB565 pixel blending
* @param fg The foreground color in uint16_t RGB565 format
* @param bg The background color in uint16_t RGB565 format
* @param alpha The alpha in range 0-255
**/
color alphaBlendRGB565( uint32_t fg, uint32_t bg, uint8_t alpha ){
alpha = ( alpha + 4 ) >> 3;
bg = (bg | (bg << 16)) & 0b00000111111000001111100000011111;
fg = (fg | (fg << 16)) & 0b00000111111000001111100000011111;
uint32_t result = ((((fg - bg) * alpha) >> 5) + bg) & 0b00000111111000001111100000011111;
return (uint16_t)((result >> 16) | result);
}
我在 Chris Chua 对 Adafruit Arduino 帧缓冲库的拉取请求中找到了这个解决方案。这是一个扩展版本,带有注释来解释数学:
// Fast RGB565 pixel blending
// Found in a pull request for the Adafruit framebuffer library. Clever!
// https://github.com/tricorderproject/arducordermini/pull/1/files#diff-d22a481ade4dbb4e41acc4d7c77f683d
color alphaBlendRGB565( uint32_t fg, uint32_t bg, uint8_t alpha ){
// Alpha converted from [0..255] to [0..31]
alpha = ( alpha + 4 ) >> 3;
// Converts 0000000000000000rrrrrggggggbbbbb
// into 00000gggggg00000rrrrr000000bbbbb
// with mask 00000111111000001111100000011111
// This is useful because it makes space for a parallel fixed-point multiply
bg = (bg | (bg << 16)) & 0b00000111111000001111100000011111;
fg = (fg | (fg << 16)) & 0b00000111111000001111100000011111;
// This implements the linear interpolation formula: result = bg * (1.0 - alpha) + fg * alpha
// This can be factorized into: result = bg + (fg - bg) * alpha
// alpha is in Q1.5 format, so 0.0 is represented by 0, and 1.0 is represented by 32
uint32_t result = (fg - bg) * alpha; // parallel fixed-point multiply of all components
result >>= 5;
result += bg;
result &= 0b00000111111000001111100000011111; // mask out fractional parts
return (color)((result >> 16) | result); // contract result
}