Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];

Вниз

Смешать два цвета с альфой   Найти похожие ветки 

 
Димка На   (2012-02-27 14:45) [0]

Может у кого-то есть уже готовая формула.
Есть два цвета с альфой (R1, G1, B1, A1) и (R2, G2, B2, A2). Необходимо их смешать и получить некий третий цвет  (R3, G3, B3, A3).

Эффект от наложения цвета  (R3, G3, B3, A3) должен быть таким, как будто сначала наложили  (R1, G1, B1, A1), а потом (R2, G2, B2, A2).

Формула, мне кажется простая должна быть, но с ходу в голову не приходит, а в инете пока не нашел.


 
Dimka Maslov ©   (2012-02-27 14:50) [1]

Цветовое пространство RGB в принципе не может дать правильного смешения цветов. Надо сначала привести в HSL (HSB) а там уже мешать как хочешь.
Закачай себе исходники от Inkscape. Там наверняка есть пример, ибо они активно занимаются цветосмешением.


 
MBo ©   (2012-02-27 14:50) [2]

http://en.wikipedia.org/wiki/Alpha_compositing


 
2222   (2012-02-27 15:17) [3]

если не найдешь дам завтра вечером готовый пример. Сам не так давно озадачивался этим


 
Андреевич   (2012-02-27 16:05) [4]

если пример нужен - то вот тут http://forum.sources.ru/index.php?showtopic=330729&st=0&#entry2888880 , правда точно не знаю насколько верна употребляемая там формула


 
Димка На   (2012-02-27 17:41) [5]

Смотрю по-порядку из http://en.wikipedia.org/wiki/Alpha_compositing
Первая формула:
http://upload.wikimedia.org/wikipedia/en/math/3/c/3/3c377902304f3e4c105ad360abbbc180.png

Co = Ca*Aa + Cb*Ab*(1-Aa)
Ao = Aa + Ab*(1-Aa)

Где Cx - цветовая компонента (от 0 до 255)
Ax - значение альфы (0 - прозрачная, 1 - непрозрачная).

К примеру, нужно смешать прозрачный цвет и полупрозрачный красный:
(0,0,0,0) и (255,0,0,0.5) (формат RGBA)
Результат для красного цвета:
Co = Ca*Aa + Cb*Ab*(1-Aa) = 255*0.5 + 0*0*(1-0.5) = 127
Ao = Aa + Ab*(1-Aa) = 0.5 + 0*(1 - 0.5) = 0.5
В итоге получили цвет (127,0,0,0.5). А по идее должно быть (255,0,0,0.5) Вопрос: почему по этой формуле потемнел цвет?


> 2222   (27.02.12 15:17) [3]

ok, спасибо.


> Андреевич   (27.02.12 16:05) [4]

Буду смотреть


 
Димка На   (2012-02-27 18:10) [6]


> Димка На   (27.02.12 17:41) [5]

Не прочитал далее. Вроде получилось как надо, осталось только в целые числа все перевести. =)

Спасибо.


 
Димка На   (2012-02-28 12:34) [7]

Готово.

Получил следующие формулы для целый чисел:

Альфа:
Ao := (Aa*N + Ab*N - Aa*Ab) div N;

Цветовая компонента:
if Ao > 0 then
 Co := (Ca*Aa*N + Cb*Ab*N - Cb*Ab*Aa) div (Aa * N + Ab * N - Aa * Ab)
else
 Co := 0; // или любое другое


N = 255.


 
Dimka Maslov ©   (2012-02-28 13:16) [8]

Лучше всё-таки работать с вещественными числами, преобразовывая в целые в самом конце. Иначе можно получить т.н. gradient banding


 
Димка На   (2012-02-28 14:00) [9]


> Лучше всё-таки работать с вещественными числами

Согласен, но это сказывается на скорости, а она имеет большое значение, т. к. формулу надо применять очень много раз. Я вынес в числитель все возможные делители знаменателя, что дает более высокую точность при целочисленном делении. В итоговой формуле я и количество умножений сократил насколько мог:


function Alpha(Down, Up: TRGBQuad; UpAlpha: Integer): TRGBQuad;
var
 AB: Integer;
 Ap, X: Integer;
begin
 if UpAlpha < 0 then UpAlpha := 0;
 if UpAlpha > 255 then UpAlpha := 255;

 if UpAlpha < 255 then
   Up.rgbReserved := Up.rgbReserved * UpAlpha div 255;

 AB := Up.rgbReserved * Down.rgbReserved;
 Ap := Up.rgbReserved * 255;
 X := Down.rgbReserved * 255 - AB;

 Result.rgbReserved := (Ap + X) div 255;
 if Result.rgbReserved > 0 then
 begin
   Result.rgbBlue := (Up.rgbBlue * Ap + Down.rgbBlue * X) div (Ap + X);
   Result.rgbGreen := (Up.rgbGreen * Ap + Down.rgbGreen * X) div (Ap + X);
   Result.rgbRed := (Up.rgbRed * Ap + Down.rgbRed * X) div (Ap + X);
 end;
end;


 
Андреевич   (2012-02-28 14:05) [10]

в случае с функцией может было бы лучше передавать данные по ссылке


 
Димка На   (2012-02-28 14:24) [11]


> Андреевич   (28.02.12 14:05) [10]
> в случае с функцией может было бы лучше передавать данные
> по ссылке

Почему?


 
Андреевич   (2012-02-28 17:08) [12]

чтобы копию не пересылать


 
Димка На   (2012-02-28 17:48) [13]


> Андреевич   (28.02.12 17:08) [12]
> чтобы копию не пересылать

Объявил параметры как Const. Но TRGBQuad - занимает 4 байта, разумно предположить, что времени на их копирование не особо много тратится.


 
2222   (2012-02-28 18:36) [14]

мх оказывается я для RGBA еще не запилил. Зато запилил для CMYKA. Вот собсно что у меня получилось. Это очень похоже на то что делает фотошоп и корел

TFloat  t,iasd,outASD;
byte BT (Это для антиалиазинга, может принимать значение от 0 до 255)
TFloat TransparensyPercent (Это с какой прозрачностью накладывать текущий цвет на предыдущий может принимать значение от 0 до 1);
TFloat DivConst=0.003921568627450980392156862745098; (константа (для замены деления на умножение))
byte Aback (альфа канал нижнего цвета. Значения от 0 до 255)

t = BT * TransparensyPercent*DivConst;
TFloat tra= 1-t;
iasd =Aback *DivConst;
outASD = iasd+(1-iasd)*t;

/////////////////////////////////////////////Это повторить для каждого канала/////////////////
byte ColorOut = Round ((color*t+colorBack*iasd*t*tra)/outASD); ////////////////////
//////////////// где color и  colorBack тип byte (значения от 0 до 255)//////////////
//////////////причем colorBack нижний, color верхний//////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

byte AOut = outASD *255


 
2222   (2012-02-28 18:38) [15]

не думаю что есть какиенить принципиальные отличия от RGBA


 
Дмитрий С ©   (2012-02-28 22:27) [16]


> 2222   (28.02.12 18:36) [14]

Это на чем?
А что там для антиалиасинга? Как он тут вообще применим?

На первый взгляд так же самая формула:)


 
antonn ©   (2012-02-28 22:36) [17]

я бы подумал на шарп, но "round()" не по-шарповски написан :)


 
DVM ©   (2012-02-28 22:38) [18]

в формулах выше надо обязательно работать с целыми числами, избавиться по возможности от умножений и div в пользу операций сдвига, иначе все это будет еле ползать.

Вот когда то для похожей задачи делал функцию смешения цветов TColor


function MixerColors(FromColor, ToColor: TColor; Rate: Single): TColor;
var
 ra, rb: byte;
begin
 RA := round(Rate * 255);
 RB := 255 - RA;
 result := (((Lo(FromColor) * RA + Lo(ToColor) * RB) shr 8) or
           (((Lo(FromColor shr 8) * RA + Lo(ToColor shr 8) * RB) shr 8) shl 8) or
           (((Lo(FromColor shr 16) * RA + Lo(ToColor shr 16) * RB) shr 8) shl 16));
end;


 
Inovet ©   (2012-02-28 22:51) [19]

> [18] DVM ©   (28.02.12 22:38)
> избавиться по возможности от умножений и div в пользу операций сдвига

Умножение теперь быстрое. div не помню.


 
Дмитрий С ©   (2012-02-29 00:22) [20]


> DVM ©   (28.02.12 22:38) [18]

Согласен. Но это немного не то смешение.
В моем случае несколько сложнее: если умножение на 255 можно заменить сдвигом и вычитанием, то деление на 255 сдвигом не заменишь (


 
2222   (2012-02-29 06:14) [21]


> Это на чем?А что там для антиалиасинга? Как он тут вообще
> применим?На первый взгляд так же самая формула:)


> antonn ©   (28.02.12 22:36) [17]
> я бы подумал на шарп, но "round()" не по-шарповски написан
> :)


Этот код "выдран" из рабочего проекта. Написан был на С++. Этот же пример я упростил до удобочитаемого, но рабочего варианта (а нафиг тут классы и структуры, которые я описывать не собираюсь). То что  для антиалиазинга - на самом деле был массив "прозрачностей". В данном случае можно поставить 255 или переработать и исключить - мне лениво.
А так да. Это действительно пример из вики.

Есмли уж кому совсем интересно. вот полный код.

void BlendLineExCMYKA(vector <long> Src, TBufferPaint& Dst,int xn,int yn,int Count,long M)
{
 int i = 0;
 TCMYK colorM;
 colorM= *PCMYK(&M);
 TFloat  t,iasd,outASD;

  // exit;
 for (int x = xn ;x<=xn + Count - 1;++x)
 {

    t = byteW(Src[i] >> 24) * TransparensyPercent*DivConst;
    TFloat tra= 1-t;
    iasd =PCMYKA(Dst.buffer)[getindex(x,yn,Dst.Width)].A *DivConst;
    outASD = iasd+(1-iasd)*t;
    int ida=getindex(x,yn,Dst.Width);
  // tpl = DivConst * t;
  // tmin=DivConst * (255 - t);

   if (Overprint_fill)
   {
/*    if (t >= 251)
     {
       if (colorM.C > 0) PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].C = colorM.C;
       if (colorM.M > 0) PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].M = colorM.M;
       if (colorM.Y > 0) PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].Y = colorM.Y;
       if (colorM.K > 0) PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].K = colorM.K;
    }

     else
       if (t > 0)
       {
         if (colorM.C > 0)  PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].C = CMYKLIB_Round(colorM.C * tpl);
         if (colorM.M > 0)  PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].M = CMYKLIB_Round(colorM.M * tpl);
         if (colorM.Y > 0)  PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].Y = CMYKLIB_Round(colorM.Y * tpl);
         if (colorM.K > 0)  PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].K = CMYKLIB_Round(colorM.K * tpl);
       }  */
   }
   else

    /* if (t >= 251)
     {
      PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)] = *PCMYK(&M);
     }

     else */
       if (t > 0)
       {

         PCMYKA(Dst.buffer)[ida].C = CMYKLIB_Round((colorM.C * t + PCMYKA(Dst.buffer)[ida].C * iasd*tra)/outASD);
         PCMYKA(Dst.buffer)[ida].M = CMYKLIB_Round((colorM.M * t + PCMYKA(Dst.buffer)[ida].M * iasd*tra)/outASD);
         PCMYKA(Dst.buffer)[ida].Y = CMYKLIB_Round((colorM.Y * t + PCMYKA(Dst.buffer)[ida].Y * iasd*tra)/outASD);
         PCMYKA(Dst.buffer)[ida].K = CMYKLIB_Round((colorM.K * t + PCMYKA(Dst.buffer)[ida].K * iasd*tra)/outASD);
         PCMYKA(Dst.buffer)[ida].A = outASD *255;
       //  PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].M = (CMYKLIB_Round(colorM.M * tpl + (PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].M * tmin)));
       //  PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].Y = (CMYKLIB_Round(colorM.Y * tpl + (PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].Y * tmin)));
       //  PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].K = (CMYKLIB_Round(colorM.K * tpl + (PCMYK(Dst.buffer)[getindex(x,yn,Dst.Width)].K * tmin)));
       }
   i+=1;
 }
}


 
2222   (2012-02-29 06:21) [22]

да. ползает вполне прилично имхо


 
2222   (2012-02-29 06:39) [23]


> в формулах выше надо обязательно работать с целыми числами,
>  избавиться по возможности от умножений и div в пользу операций
> сдвига, иначе все это будет еле ползать.


Быстрее то оно быстрее. Но результат будет отличаться.


 
Омлет ©   (2012-02-29 08:50) [24]


> 2222   (29.02.12 06:21) [22]
> да. ползает вполне прилично имхо

Есть куда оптимизировать.


 
Димка На   (2012-02-29 11:07) [25]


> 2222   (29.02.12 06:39) [23]
>
> > в формулах выше надо обязательно работать с целыми числами,
>
> >  избавиться по возможности от умножений и div в пользу
> операций
> > сдвига, иначе все это будет еле ползать.
>
>
> Быстрее то оно быстрее. Но результат будет отличаться.

Будет отличаться, да, если взять те же формулы что и для действительных чисел, а если их преобразовать - результат не будет значительно отличаться (будет таким, если в твоих формулах round заменить на floor).


> Омлет ©   (29.02.12 08:50) [24]
>
> > 2222   (29.02.12 06:21) [22]
> > да. ползает вполне прилично имхо
> Есть куда оптимизировать.

Delphi тоже такой код генерирует, то его есть куда оптимизировать. Если захочется прям реактивности, придется на асме переписать.


 
2222   (2012-02-29 12:29) [26]


> Омлет ©   (29.02.12 08:50) [24]
> > 2222   (29.02.12 06:21) [22]> да. ползает вполне прилично
> имхо
>Есть куда оптимизировать.


Согласен. Уже сейчас вижу четыре одинаковых умножения ... как раньше не заметил...


 
Sapersky   (2012-02-29 14:38) [27]

Если есть несколько делений на одно и то же, то можно один раз разделить, остальное заменить умножением. Для плавающей точки это элементарно, для целых так:
Z = X / Y = (X / Y) * (65536 / 65536) = X * (65536 / Y) / 65536 = X * (65536 / Y) shr 16
65536 / Y считается заранее.
Точность при этом получается несколько хуже, чем с "честным" делением. Если взять сдвиг больше (20, 24), точность будет выше, но X * (65536 / Y) не должно выходить за пределы Integer.

Вызов функции для каждого пикселя нежелателен. Либо инлайн, либо один вызов на скан-линию.

По переводу на целые - в общем да, хотя был в соседнем полку противуположный случай:
http://blog.lexa.ru/2011/08/27/o_legacy_i_formatakh_dannykh.html
Случай довольно специфический, конечно - 16-битные картинки, высокие требования к точности. С обычными 8-битными картинками, наверное, лучше по старинке, да и SSE не все могут так запросто применять.


 
2222   (2012-02-29 16:55) [28]

Перечитывал ветку, в [14] заметил ошибку

byte ColorOut = Round ((color*t+colorBack*iasd*t*tra)/outASD);

Читать как

byte ColorOut = Round ((color*t+colorBack*iasd*tra)/outASD);



Страницы: 1 вся ветка

Форум: "Прочее";
Текущий архив: 2013.03.22;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.53 MB
Время: 0.056 c
2-1338391814
Serge Petrov
2012-05-30 19:30
2013.03.22
TWebBrowser и обновление элемента веб-страницы


15-1332595957
StusentGuse
2012-03-24 17:32
2013.03.22
Прокся не на 80 порт


2-1331031997
Scott Storch
2012-03-06 15:06
2013.03.22
приводить к типу DWORD


15-1331817337
QWERTY_
2012-03-15 17:15
2013.03.22
thumbnails


2-1337453528
novichek
2012-05-19 22:52
2013.03.22
показать форму на втором мониторе





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский