Re: RGBA -> окончательный RGB
От: WinterMute Россия http://yarrr.ru
Дата: 16.09.05 12:01
Оценка:
Здравствуйте, muradm, Вы писали:

M>Когда мы используем цвет с альфой, то есть RGBA

M>И делаем SetPixel точки такого цвета на некоторую другую точку, то в итоге получаем точку некоторого 3-го цвета.

M>Как я понимаю в конечном итоге монитор показывает эту точку в формате RGB, то есть без альфы.

M>Так вот: как получить эти окончательные RGB, имея цвет исходного пикселяи и цвет нового пикселя?

Ты имеешь в виду, как наложить один RGBA цвет на другой? Я делал так:

    // Шаблон для наложения цвета colorOver на фон (bg). 
    template< bool t_bUseR  // Накладывать красную компоненту
            , bool t_bUseG  // Накладывать зелёную компоненту
            , bool t_bUseB  // Накладывать синюю компоненту
            , bool t_bUseA  // Изменять значение альфаканала
            , bool t_bBgIsOpaque // полагать, что bg.A() == 255
            , bool t_bUseScale   // домножать colorOver.A() на scale
            >
    inline void blendColors_T( const Color& bg, const Color& colorOver, Color& dst, byte scale = 255 )
    {
        uint topA = colorOver.A();

        if( t_bUseScale )
        {
            topA *= scale;
            topA /= 255;
        }

        const uint err = 255/2;

        switch( topA )
        {
        case 255:
            dst = colorOver; return;
        case 0:
            dst = bg; return;
        default:
            uint a3, a4;

            if( t_bBgIsOpaque )
            {
                a3 = uint(255) - topA;
                a4 = topA;
            }
            else
            {
                uint k  = ( uint( uint(255)-topA ) * bg.A() + err ) / 255;

                uint a2 = (topA + k);

                a3 = (k*uint(255))/a2;
                a4 = uint(255)-a3;

                if( t_bUseA ) dst.A() = a2;
            }

            if( t_bUseR ) dst.R() = (bg.R()*a3 + colorOver.R()*a4 + err) / 255;
            if( t_bUseG ) dst.G() = (bg.G()*a3 + colorOver.G()*a4 + err) / 255;
            if( t_bUseB ) dst.B() = (bg.B()*a3 + colorOver.B()*a4 + err) / 255;

            return;
        }
    }
}


Здесь используется математически правильная формула, сделав небольшое допущение можно ускорить код примерно в два раза -- для этого нужно заменить "/ 255" на ">> 8", ещё можно отказаться от округления в ближайшую сторону отбросив "+ err".

Собственно о параметрах: t_bUseR, t_bUseG, t_bUseB -- какие каналы изображения накладывать, от такой гибкости можно и отказаться, впрочем, на производительность она никак не влияет.
t_bUseA -- после наложения цветов альфаканал меняется, если вдруг не хочется перезаписывать альфаканал, то нужно установить этот параметр в false.
t_bBgIsOpaque -- предпологать что прозрачность фонового цвета равна 255, в этом случае вычисления значительно упрощаются.
t_bUseScale -- домножать прозрачность накладываемого цвета на коэфициент (scale/255), полезно, если хочется на лету делать накладываемый цвет полупрозрачным.

Color& bg -- фоновый цвет;
Color& colorOver -- накладываемый цвет;
Color& dst -- цвет, куда сохраняется результат, может совпадать с bg;

Теперь, допустим у тебя есть два цвета bg = RGBA(100,100,100,100) и colorOver = RGBA(200,200,200,200), после их наложения результирующий цвет тоже будет полупрозрачным (только если альфа bg не равна 255), т.е., в общем случае придётся наложить полученный цвет на цвет, который считается фоновым, например цвет диалога. Но если цвет bg заведомо непрозрачен, то можно обойтись только одним наложением (лучше с флагом t_bBgIsOpaque).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.