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/
dest_r = (unsigned char)((float)dest_r * (1-a/255.0f) + (float)src_r * a/255);
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
> 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ę
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
> 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
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
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
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/