From dee61e62398a5a0a5424e206e8e603265873fad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Dec 2020 12:48:06 +0100 Subject: [PATCH] config: fix rounding error when calculating background color with alpha MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We use pre-multiplied alpha color channels, but were having bad rounding errors due to the alpha divider being truncated to an integer. The algorithm for pre-multiplying a color channel is: alpha_divider = 0xffff / alpha pre_mult_color = color / alpha_divider In order to fix the rounding errors, we could turn ‘alpha_divider’ into a double. That however would introduce a performance penalty since now we’d need to do floating point math for each cell. The algorithm can be trivially converted to: pre_mult_color = color * alpha / 0xffff Since both color and alpa values are < 65536, the multiplication is “safe”; it will not overflow an uint32_t. --- config.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/config.c b/config.c index 743a1a3..989bc2e 100644 --- a/config.c +++ b/config.c @@ -53,16 +53,12 @@ conf_to_color(const struct yml_node *node) uint16_t blue = hex_byte(&hex[4]); uint16_t alpha = hex_byte(&hex[6]); - if (alpha == 0) - return (pixman_color_t){0, 0, 0, 0}; - alpha |= alpha << 8; - int alpha_div = 0xffff / alpha; return (pixman_color_t){ - .red = (red << 8 | red) / alpha_div, - .green = (green << 8 | green) / alpha_div, - .blue = (blue << 8 | blue) / alpha_div, + .red = (red << 8 | red) * alpha / 0xffff, + .green = (green << 8 | green) * alpha / 0xffff, + .blue = (blue << 8 | blue) * alpha / 0xffff, .alpha = alpha, }; }