Форум: "Прочее";
Текущий архив: 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.062 c