Есть буфер с альфа каналом, ну и цвета с альфа составляющей.
Не могу их правильно смешать
Уже весь мозг себе сломал.
Основная идея в том, что если буфер залит непрозрачным цветом, то цвета должны смешиваться с фоном,
а если фон прозрачный, то не должны.
Первый используется для отображения на экране, второй для генерации прозрачных картинок.
Пока что велосипед выглядит так:
static AGG_INLINE void blend_pix(value_type* p,
unsigned cr, unsigned cg, unsigned cb,
unsigned alpha,
unsigned cover=0)
{
if(alpha==0)return;
calc_type a=p[Order::A];
if(a==0)
{
p[Order::R]=(value_type)cr;
p[Order::G]=(value_type)cg;
p[Order::B]=(value_type)cb;
p[Order::A]=(value_type)alpha;
return;
}
calc_type r = p[Order::R];
calc_type g = p[Order::G];
calc_type b = p[Order::B];
p[Order::R] = (value_type)((cr*alpha*base_mask+r*a*(base_mask-alpha))/base_mask/base_mask);
p[Order::G] = (value_type)((cg*alpha*base_mask+g*a*(base_mask-alpha))/base_mask/base_mask);
p[Order::B] = (value_type)((cb*alpha*base_mask+b*a*(base_mask-alpha))/base_mask/base_mask);
p[Order::A] = (value_type)((alpha + a) - ((alpha * a + base_mask) >> base_shift));
}
Использую agg. Но это в принципе не важно. Давайте будем считать
что цвета у нас от 0 до 1. И альфа канал тоже от 0 до 1.
r,g,b,a — цвет фона.
cr,cg,cb,ca — накладываемый сверху цвет.
Код у нас получается такой:
if(ca==0)return;
if(a==0)
{
r=cr;
g=cg;
b=cb;
a=ca;
return;
}
r=cr*ca+r*a*(1.0-ca);
g=cg*ca+g*a*(1.0-ca);
b=cb*ca+b*a*(1.0-ca);
a=ca+a-ca*a;
Первый if: Если новый цвет целиком прозрачный, то он не оказывает никакого влияния на буфер, даже если тот тоже целиком прозрачный.
Второй if — это костыль.
Посмотрим на выражение: r=cr*ca+r*a*(1.0-ca);
Допустим буфер у нас целиком прозрачный, тогда правая часть суммы r*a*(1.0-ca)=0.
Допустим мы накладываем белый цвет, с прозрачностью 0.5. Тогда левая часть суммы, превращает наш цвет из белого в серый.
Хотя по идее он должен был остаться белым с альфа каналом 0.5.
Эту формулу взял с
википедии. Мне она показалась наиболее разумной в моём случае.
Формула смешивания альфа канал хз почему такая.
Взял с той же википедии и в том же agg часто используется именно в таком виде.
Что она значит, так и не понял
Для меня это "суммируем значение прозрачности, главное чтобы она не превысила максимальное значение"
В чём проблема с моим кодом?
1. Хотелось бы убрать костыли и не изобретать велосипед.
2. Хотелось бы добиться непрерывности функций.
Ещё раз приведу формулу: r=cr*ca+r*a*(1.0-ca);
Допустим буфер у нас "почти прозрачный" a — стремится к нулю, тогда правая часть суммы r*a*(1.0-ca) тоже стремиться к нулю.
И мы получаем туже проблему, что белый полупрозрачный цвет превращается в грязно-серый.
3. Хотелось бы понять как именно складывать альфа канал.
Здравствуйте, Шебеко Евгений, Вы писали:
ШЕ>Есть буфер с альфа каналом, ну и цвета с альфа составляющей.
ШЕ>Не могу их правильно смешать Уже весь мозг себе сломал.
ШЕ>Основная идея в том, что если буфер залит непрозрачным цветом, то цвета должны смешиваться с фоном,
ШЕ>а если фон прозрачный, то не должны.
ШЕ>Первый используется для отображения на экране, второй для генерации прозрачных картинок.
ШЕ>Пока что велосипед выглядит так:
ШЕ>ШЕ>static AGG_INLINE void blend_pix(value_type* p,
ШЕ> unsigned cr, unsigned cg, unsigned cb,
ШЕ> unsigned alpha,
ШЕ> unsigned cover=0)
ШЕ>{
ШЕ> if(alpha==0)return;
ШЕ> calc_type a=p[Order::A];
ШЕ> if(a==0)
ШЕ> {
ШЕ> p[Order::R]=(value_type)cr;
ШЕ> p[Order::G]=(value_type)cg;
ШЕ> p[Order::B]=(value_type)cb;
ШЕ> p[Order::A]=(value_type)alpha;
ШЕ> return;
ШЕ> }
ШЕ> calc_type r = p[Order::R];
ШЕ> calc_type g = p[Order::G];
ШЕ> calc_type b = p[Order::B];
ШЕ> p[Order::R] = (value_type)((cr*alpha*base_mask+r*a*(base_mask-alpha))/base_mask/base_mask);
ШЕ> p[Order::G] = (value_type)((cg*alpha*base_mask+g*a*(base_mask-alpha))/base_mask/base_mask);
ШЕ> p[Order::B] = (value_type)((cb*alpha*base_mask+b*a*(base_mask-alpha))/base_mask/base_mask);
ШЕ> p[Order::A] = (value_type)((alpha + a) - ((alpha * a + base_mask) >> base_shift));
ШЕ>}
ШЕ>
ШЕ>Использую agg. Но это в принципе не важно. Давайте будем считать
ШЕ>что цвета у нас от 0 до 1. И альфа канал тоже от 0 до 1.
ШЕ>r,g,b,a — цвет фона.
ШЕ>cr,cg,cb,ca — накладываемый сверху цвет.
ШЕ>Код у нас получается такой:
ШЕ>ШЕ>if(ca==0)return;
ШЕ>if(a==0)
ШЕ>{
ШЕ> r=cr;
ШЕ> g=cg;
ШЕ> b=cb;
ШЕ> a=ca;
ШЕ> return;
ШЕ>}
ШЕ>r=cr*ca+r*a*(1.0-ca);
ШЕ>g=cg*ca+g*a*(1.0-ca);
ШЕ>b=cb*ca+b*a*(1.0-ca);
ШЕ>a=ca+a-ca*a;
ШЕ>
ШЕ>Первый if: Если новый цвет целиком прозрачный, то он не оказывает никакого влияния на буфер, даже если тот тоже целиком прозрачный.
ШЕ>Второй if — это костыль.
ШЕ>Посмотрим на выражение: r=cr*ca+r*a*(1.0-ca);
ШЕ>Допустим буфер у нас целиком прозрачный, тогда правая часть суммы r*a*(1.0-ca)=0.
ШЕ>Допустим мы накладываем белый цвет, с прозрачностью 0.5. Тогда левая часть суммы, превращает наш цвет из белого в серый.
ШЕ>Хотя по идее он должен был остаться белым с альфа каналом 0.5.
ШЕ>Эту формулу взял с википедии. Мне она показалась наиболее разумной в моём случае.
ШЕ>Формула смешивания альфа канал хз почему такая. Взял с той же википедии и в том же agg часто используется именно в таком виде.
ШЕ>Что она значит, так и не понял Для меня это "суммируем значение прозрачности, главное чтобы она не превысила максимальное значение"
ШЕ>В чём проблема с моим кодом?
ШЕ>1. Хотелось бы убрать костыли и не изобретать велосипед.
ШЕ>2. Хотелось бы добиться непрерывности функций.
ШЕ>Ещё раз приведу формулу: r=cr*ca+r*a*(1.0-ca);
ШЕ>Допустим буфер у нас "почти прозрачный" a — стремится к нулю, тогда правая часть суммы r*a*(1.0-ca) тоже стремиться к нулю.
ШЕ>И мы получаем туже проблему, что белый полупрозрачный цвет превращается в грязно-серый.
ШЕ>3. Хотелось бы понять как именно складывать альфа канал.
1. Зачем ты два раза на base_mask делишь каждую составляющую?
2. имхо надо рассматривать относительные прозрачности, т.е если даные {r1,g1,b1,a1},{r2,g2,b2,a2}, то при а2>a1
r = (r1*a1 + r2(a2-a1))/a2 , если a1 < a2
r = (r2*a2 + r1*(a1-a2))/a2. Тогда таких артефактов с грязно серым можно избежать.
Здравствуйте, Шебеко Евгений, Вы писали:
<>
Представим себе, что у нас трёхслойная картинка: полностью непрозрачный задний план z.{r,g,b} и два полупрозрачных слоя, которые мы накладываем поверх — сперва первый y.{r,g,b,a}, затем второй x.{r,g,b,a}
Рассмотрим один цветовой канал; назовём его, для краткости z, y, x, а альфа-каналы, соответственно, ya, xa
Посмотрим, что получается.
Накладываем первый слой
z' = z·(1-ya) + y·ya
Накладываем второй слой
z'' = (z·(1-ya) + y·ya)·(1-xa) + x·xa
z'' = z·(1-ya)·(1-xa) + y·ya·(1-xa) + x·xa
Попробуем найти эквивалент (t,ta) этих двух слоёв
z'' = z·(1-ta) + t·ta
1-ta = (1-ya)·(1-xa)
t·ta = y·ya·(1-xa) + x·xa
Откуда, элементарно, получаем
ta = 1-(1-ya)·(1-xa) = xa+ya-xa·ya
t = (y·ya·(1-xa) + x·xa)/ta
Если xa=ya=0, то t=0/0 — сингулярность, из-за полной прозрачности нам побарабану, какой там цвет.
Если ya=0, то ta=xa, t=x
Если xa=0, то ta=ya, t=y
Вроде бы, всё логично получается.
Если x=y=v, то t=(v·ya·(1-xa)+v·xa)/ta = v·(xa+ya-xa·ya)/ta = v·ta/ta = v
Вуаля! Цвет сохранился!
Блин, супер!
Всё просто и доходчиво.
Проверил — работает!
С антиалиасингом и без.
Самое прикольное, что фон у этих картинок зелёный-прозрачный.
Это видно, если открыть файлы в Paint.
А тут нет даже намёка на зелёный. Вообщем cool