Вот несколько примеров функций, которые выполняют предварительное умножение альфа, а затем альфа-композицию с предварительно умноженным альфа, обе из которых имеют некоторый вариант этой проблемы. Они предназначены для RGBA/BGRA с прямым порядком байтов, давайте пока проигнорируем проблемы с порядком байтов:
Код: Выделить всё
uint32_t premultiply_alpha(uint32_t color)
{
uint32_t alpha = ((color>>24) & 0xff);
uint32_t rb = color & 0xff00ffU;
uint32_t g = color & 0x00ff00U;
rb = (rb * alpha) >> 8;
g = (g * alpha) >> 8;
return (color & 0xff000000) | (rb & 0xff00ff) | (g & 0x00ff00);
}
Код: Выделить всё
uint32_t rgb_blend(uint32_t background, uint32_t color)
{
uint32_t alpha = color>>24;
uint32_t rb = background & 0xff00ffU;
uint32_t g = background & 0x00ff00U;
rb = (color & 0xff00ff) + ((0xff - alpha) * rb >> 8);
g = (color & 0x00ff00) + ((0xff - alpha) * g >> 8);
uint32_t dest_alpha = background>>24;
dest_alpha = alpha + dest_alpha - ((alpha*dest_alpha) >> 8);
dest_alpha = dest_alpha < 255 ? dest_alpha : 255;
return (dest_alpha > 8 эквивалентно делению на 0x100, а не на 0xff. Поэтому замена сдвига на /0xff решает проблему. Однако, поскольку делитель является постоянным, компилятор будет избегать использования инструкции div, используя трюк из главы 10 Hacker's Delight (я подтвердил, что GCC делает это даже без включенной оптимизации). Эмпирически кажется, что обрабатывать r и b в одном регистре, как я это делал здесь, при наличии такой оптимизации небезопасно. Мне все еще интересно узнать, существуют ли «стандартные» решения этой проблемы/то, что обычно делают другие люди.
Подробнее здесь: [url]https://stackoverflow.com/questions/78686806/alpha-compositing-in-fixed-point-0xff-is-not-1-0[/url]