Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

kanal alpha

6 views
Skip to first unread message

przezroczystek

unread,
May 13, 2004, 4:11:54 AM5/13/04
to
Witam

Są dane dwie bitmapy w postaci tablic z kolorami RGB.
Jedna bitmapa jest tłem a druga jest bitmapą którą na
to tło trzeba przekopiować. Dodatkowo jest dana tablica
liczb o rozmiarze odpowiadającej bitmapie kopiowanej na
tło, w której poszczególne liczby opisują jak mocno
poszczególne piksele mają pokryć tło. Taki zwykły kanał alpha.
Jednak konwersja na dane zmienno przecinkowe z pewnością
bedzie mało wydajna, ja to napisałbym tak:

unsigned char dest_r;
unsigned char dest_g;
unsinged char dest_b;

unsigned char src_r;
unsigned char src_g;
unsigned char src_b;

unsigned char a; // wartość alpha

dest_r = (unsigned char)((float)dest_r * (float)(255-a) + (float)src_r *
(float)a);

dest_g = (unsigned char)((float)dest_g * (float)(255-a) + (float)src_b *
(float)a);

dest_b = (unsigned char)((float)dest_b * (float)(255-a) + (float)src_b *
(float)a);


Można jakoś szybciej coś takiego wyliczyć?
Z góry dziękuję
pozdrawiam

--
Wysłano z serwisu Usenet w portalu Gazeta.pl -> http://www.gazeta.pl/usenet/

przezroczystek

unread,
May 13, 2004, 4:17:52 AM5/13/04
to

chyba jeszcze trzeba przeskalować przedział <0,255> w przedział <0.0f,1.0f>

dest_r = (unsigned char)((float)dest_r * (1-a/255.0f) + (float)src_r * a/255);

Wojciech Mula

unread,
May 13, 2004, 4:36:35 AM5/13/04
to
przezroczystek wrote:
> unsigned char dest_r;
> unsigned char dest_g;
> unsinged char dest_b;
>
> unsigned char src_r;
> unsigned char src_g;
> unsigned char src_b;
>
> unsigned char a; // wartość alpha
>
> dest_r = (unsigned char)((float)dest_r * (float)(255-a) + (float)src_r *
> (float)a);

I to działa? Np. jeśli dest_r==255, a==0 to wynik wyniesie
255*255 = 65025, a to z pewnością przekracza zakres 'unsigned char'.

Ale masz prawie dobrze, tylko zupełnie niepotrzebnie konwertujesz na float.
Wystarczy:

dest_r = (dest_r * (unsigned int)(255-a) + src_r * (unsigned int)a) >> 8;

Jeśli 'a' byłoby stałe dla całego obrazu, to możesz prekalkulować wyniki
mnożenia; składowe RGB mają zakres od 0 do 255, potrzebujesz dwóch tablic
odpowiadających mnożeniu przez a oraz 255-a. Czyli kod wyglądałby mniej
więcej tak:

unsigned char A[256], B[256];
for (int i=0; i<256; i++) {
A[i] = (i*a) >> 8;
B[i] = (i*(255-a)) >> 8;
}

for (wszystkie piksele obrazów) {
dest_składowa = B[dest_składowa] + A[src_składowa];
}

hth
Wojtek

przezroczystek

unread,
May 13, 2004, 4:50:53 AM5/13/04
to

> I to działa? Np. jeśli dest_r==255, a==0 to wynik wyniesie
> 255*255 = 65025, a to z pewnością przekracza zakres 'unsigned char'.
Oczywiście pomyliłem się, przepraszam.

> dest_r = (dest_r * (unsigned int)(255-a) + src_r * (unsigned int)a) >> 8;

Bardzo ciekawy pomysł, dziękuję :)
Swoją drogą ciekawe co kompilatory robią podczas takiej konwersji.


> Jeśli 'a' byłoby stałe dla całego obrazu, to możesz prekalkulować wyniki
> mnożenia; składowe RGB mają zakres od 0 do 255, potrzebujesz dwóch tablic
> odpowiadających mnożeniu przez a oraz 255-a. Czyli kod wyglądałby mniej
> więcej tak:
>
> unsigned char A[256], B[256];
> for (int i=0; i<256; i++) {
> A[i] = (i*a) >> 8;
> B[i] = (i*(255-a)) >> 8;
> }
>
> for (wszystkie piksele obrazów) {
> dest_składowa = B[dest_składowa] + A[src_składowa];
> }

To też ciekawy pomysł, jednak chciałem napisać coś w rodzaju
uniwersalnego komponentu do późniejszego wykorzystania. Nigdy
nie będę wiedział jaki inny komponent jest nad nim, pod nim i
chyba będę musiał za każdym razem wyliczać wszystko.

Dziękuję

Wojciech Mula

unread,
May 13, 2004, 6:04:51 AM5/13/04
to
przezroczystek wrote:
>> dest_r = (dest_r * (unsigned int)(255-a) + src_r * (unsigned int)a) >> 8;
> Bardzo ciekawy pomysł, dziękuję :)
> Swoją drogą ciekawe co kompilatory robią podczas takiej konwersji.

Jeśli dobrze optymalizują, to na procesorach x86 konwersje typów oraz
przesunięcie bitowe mogą zostać wyeliminowane, natomiast operacja 255-a
zastąpiona przez operację xor.

Ale kompilatory nie wpadną na "pomysł", jak człowiek. Poniższy pomysł
powinienem opantentować, ale jestem przeciwny patentom na pomysły. :-)

Po pewnych przekształceniach wzoru, wystarczy użyć jednej operacji mnożenia.

byte d,s,a;
d = (d*(255-a) + s*a) >> 8
d = (d*255 - d*a + s*a) >> 8;

Mnożenie d*a oraz s*a można wykonać w jednej oparacji mnożenia 32-bitowym
(oczywicie przy założeniu, że d, s i a to bajty). Na 16-bitowych procesorach
też zadziała, tylko będą "fizycznie" potrzebne 2 operacje 16-bitowego mnożenia.
Myślę, że optymistycznie możemy założyć, że tylko 0.001% użytkowników PC ma
procesor 286 lub starszy.

A więc kod wygląda tak:

dword A; // 32-bity, bez znaku
A = ((dword)d << 16) | (dword)s;
A *= (dword)a;

Teraz w starszym słowie A jest wynik mnożenia d*a, natomiast w młodszym słowie
wynik s*a. Potrzebujemy starszych bajtów poszczególnych wyników, zatem:

// byte nd, ns
nd = (A >> 24) & 0xff; // (d*a) >> 8;
ns = (A >> 8) & 0xff; // (s*a) >> 8;

Wynik ostateczny (po uwzględnieniu składnika (d*255) >> 8 - ... = d - ...)

d = d - nd + ns;

I tyle. Tzn. ten wynik nie jest dokładnie równy temu, jaki uzyskalibyśmy z
pierwszego, wyjściowego wyrażenia -- w grę wchodzą błędy zaokrągleń, na
poziomie +-1, spowodowane "przedwczesnymi" przesunięciami bitowymi (a>>8 +
b>>8) != ((a+b) >> 8), ale normalny człowiek tego nie zauważy. Chyba, że będzie
porównywał energię obrazów otrzymanych dwoma sposobami -- ale wówczas nie jest
normalny. :-)

Mój gcc 2.97 ten kod całkiem zgrabnie zoptymalizował.

hth
Wojtek

przezroczystek

unread,
May 14, 2004, 7:55:17 PM5/14/04
to
Witam :)

> nd = (A >> 24) & 0xff; // (d*a) >> 8;
> ns = (A >> 8) & 0xff; // (s*a) >> 8;

> d = d - nd + ns;

Jestem pod wrażeniem :)
Dzięki za pomoc :)
Pozdrawiam

przezroczystek

unread,
May 16, 2004, 8:28:45 AM5/16/04
to
Witam :)

A co byś powiedział o wrzuceniu liczb do zmiennej 64'czro
bitowej i wymnożenia czterech za jednym razem? :)
Niedługo będę to pisał, jestem ciekawy jak się sprawdzi...
A czy rejestry MMX nie były specjalnie do tego?
Pewnie jest w sieci coś o wykorzystaniu tych rejestrów
do podstawowych operacji graficznych...

pozdrawiam

Wojciech Mula

unread,
May 16, 2004, 9:24:29 AM5/16/04
to
przezroczystek wrote:
> A co byś powiedział o wrzuceniu liczb do zmiennej 64'czro
> bitowej i wymnożenia czterech za jednym razem? :)

Musiałbyś mieć procesor na prawdę 64-bitowy. Na 32-bitowym trzeba będzie
użyć dwóch instrukcji mnożenia, więc zysk nie będzie duży; choć kompilator
może to jakoś zoptymalizować, więc warto spróbować.

> Niedługo będę to pisał, jestem ciekawy jak się sprawdzi...
> A czy rejestry MMX nie były specjalnie do tego?

Specjalnie, to za dużo powiedziane. Ale umiejętne wykorzystanie
MMX/SSE2 może dać bardzo znaczący przyrost prędkości.

> Pewnie jest w sieci coś o wykorzystaniu tych rejestrów
> do podstawowych operacji graficznych...

Np. na mojej stronie http://wmula.republika.pl/download/asmcorner.tar.gz
(plik crossfad.html); ze dwa lata temu bawiłem się miksowaniem obrazów
i został tego ślad. Aczkolwiek to co ostatnio podesłałem na grupę jest
zupełnie świeże. :-)

Wojtek

przezroczystek

unread,
May 16, 2004, 11:53:16 AM5/16/04
to
> Musiałbyś mieć procesor na prawdę 64-bitowy. Na 32-bitowym trzeba będzie
> użyć dwóch instrukcji mnożenia, więc zysk nie będzie duży; choć kompilator
> może to jakoś zoptymalizować, więc warto spróbować.

Nie rozumiem tego asemblera na jaki zamienia to
kompilator pod Windows, ale mam wrażenie że
wywołuje on jakąś funkcję biblioteczną do mnożenia
64 bitowych liczb i działa bardzo wolno na nich.

Zrobiłem taki prosty test: http://republika.pl/przezroczystek/

0 new messages