Вот несколько примеров функций, которые выполняют предварительное умножение альфа, а затем альфа-композицию с предварительно умноженным альфа, обе из которых имеют некоторый вариант этой проблемы. Они предназначены для RGBA/BGRA с прямым порядком байтов, давайте пока проигнорируем проблемы с порядком байтов:
Код: Выделить всё
unsigned premultiply_alpha(unsigned color)
{
unsigned alpha = ((color>>24) & 0xff);
unsigned rb = color & 0xff00ffU;
unsigned g = color & 0x00ff00U;
rb = (rb * alpha) >> 8;
g = (g * alpha) >> 8;
return (color & 0xff000000) | (rb & 0xff00ff) | (g & 0x00ff00);
}
Код: Выделить всё
unsigned rgb_blend(unsigned background, unsigned color)
{
unsigned alpha = color>>24;
unsigned rb = background & 0xff00ffU;
unsigned g = background & 0x00ff00U;
rb = (color & 0xff00ff) + ((0xff - alpha) * rb >> 8);
g = (color & 0x00ff00) + ((0xff - alpha) * g >> 8);
unsigned 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]