Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2013.03.22;
Скачать: CL | DM;

Вниз

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

 
Димка На   (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;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.058 c
1-1275549451
ford
2010-06-03 11:17
2013.03.22
чтение текстовых данных из двоичных файлов (PDF)


2-1334555099
Мишан
2012-04-16 09:44
2013.03.22
Вот так бывает


2-1337494785
TStas
2012-05-20 10:19
2013.03.22
MethodName


15-1338220756
brother
2012-05-28 19:59
2013.03.22
Как Вы прочитали это: CoCu ?


15-1348271391
Rouse_
2012-09-22 03:49
2013.03.22
Не устанавливайте iOS 6