Szybsze pixele

0

Często pisząc jakiś program korzystam z Canvas->Pixels. Jak każdy wie korzystanie z tego sposobu zajmuje wiele czasu. Dlatego też postanowiłem napisać własną procedurę obsługującą piexle. Oto i te procedury:

<font color="blue">void __fastcall Setpixel(int x, int y, TColor value)
{
_EAX=line[y];
asm {
mov ebx,x
imul ebx,3

  add   eax,ebx
  mov   ebx,value

  mov   [eax+2],bl
  mov   [eax+1],bh
  shr   ebx,16
  mov   [eax],bl

}
}
TColor __fastcall Getpixel(int x, int y)
{

  _EBX=line[y];

asm {
mov eax,x
imul ax,3
add ebx,eax
mov ecx,[ebx]
mov eax,ecx

  mov   ah,0
  shl   eax,16
  mov   bx,cx

  mov   ax,bx

  shr   ecx,16
  mov   al,cl

}
}</span>Można też dodać:
<font color="blue">__property TColor pixel[int x][int y] = { read=Getpixel, write=Setpixel };</span>
Przed korzystaniem z nich należy najpierw wypełnić tablicę line[]:

<font color="blue">unsigned int* line =new unsigned int[bitmap->Height+2]; // to 2 zawszę dodaję dla bezpieczeństwa
bitmap->PixelFormat=pf24bit; // procedury działają poprawnie tylko przy 24bit głębi!
for (int a=0;aHeight;a++)
line[a]=(unsigned int) bitmap->ScanLine[a];</span> bitmap oznacza bitmapę (np. Image1->Picture->Bitmap).
Nie są to może doskonałe funkcje, ale działające znacznie szybciej niż Canvas->Pixels.
Kod nie ma żadnych zabezpieczeń (np. sprawdzanie x,y), gdyż zależało mi wyłącznie na prędkości.
Na przeprowadzonych testach wywołanie Canvas->Pixels[x][y]=Canvas->Pixels[x][y] ok. 3500000 razy zajęło 25 sekund. Analogiczny test z wykorzystaniem moich funkcji zajął 0,11 sekundy! (nie licząc czasu potrzebnego na zainicjalizowanie tablicy line[]).
UWAGA: TAKI DOSTĘP DO PIELI MOŻNA WYKORZYSTAĆ TYLKO W PRZYPADKU BITMAP 24-BITOWYCH! DLA INNYCH ROZDZIELCZOŚCI NALEŻY POZMIENIAĆ TROCHĘ KOD!

Na koniec jeszcze jedno: nie znam się za dobrze na programowaniu, więc w przypadku zauważenia jakichkolwiek błędów proszę o wyrozumiałość. Jeżeli jest ktoś zainteresowany innymi rozdzielczościami mogę mu przesłać kod mailem.

Jeżeli ktoś zna jakiś szybszy sposób bardzo bym prosił o kontakt: [email protected]

<font color="blue"></span><font color="green"></span><font color="green"></span><font color="green"></span>

0

Jeżeli to są bitmapy, to spokojnie można korzystać ze ScanLine. Zwiększenie szybkości jest także znacznie większe, a nie trzeba bawić się z dodatkowymi funkcjami (niekiedy nieczytelnymi dla nieznających asm).

0

Doskonale wiem, że można użyć ScanLine, ale jak już pisałem zależy mi na prędkości, a pisząc w asm funkcje wykonują się odrobinę szybciej niż w c++

0

Jak już tak bardzo się bawić, to ja bym w ogóle pomijał ScanLine. Przetrzymywanie tablicy nie jest najciekawszym rozwiązaniem.

Kod w Delphi, ale asm to asm :):

procedure SetPix(x, y, W: Integer; P: Pointer; Color: TColor);
asm
push edx
mov edx, eax
// eax = 3x
shl eax, 1
add eax, edx
// ecx = 3W
mov edx, ecx
shl ecx, 1
add ecx, edx
// ecx = 3W+2
add ecx, 2
xchg eax, ecx
pop edx
// eax = (3W+2)y
imul edx
// eax = -(3W+2)y
neg eax
// eax = -(3W+2)y + 3x
add eax, ecx
mov edx, P
// eax = P-(3W+2)y + 3x
add edx, eax
mov ecx, Color
shl ecx, 8
bswap ecx
mov [edx], cx
shr ecx, 8
mov [edx+2], ch
end;

function GetPix(x, y, W: Integer; P: Pointer): TColor;
asm
push edx
mov edx, eax
// eax = 3x
shl eax, 1
add eax, edx
// ecx = 3W
mov edx, ecx
shl ecx, 1
add ecx, edx
// ecx = 3W+2
add ecx, 2
xchg eax, ecx
pop edx
// eax = (3W+2)y
imul edx
// eax = -(3W+2)y
neg eax
// eax = -(3W+2)y + 3x
add eax, ecx
mov edx, P
// eax = P-(3W+2)y + 3x
add edx, eax
mov eax, [edx]
bswap eax
shr eax, 8
end;

var
Linia: Pointer;
i, j, H, W: Integer;
begin
H := Image1.Picture.Bitmap.Height;
W := Image1.Picture.Bitmap.Width;
Linia := Image1.Picture.Bitmap.ScanLine[0];
for j := 0 to H-1 do
for i := 0 to W-1 do
SetPix(i, j, W, Linia, RGB(i, j, (i+j) div 2));
Image1.Repaint;
end;

Można to oczywiście jeszcze zoptymalizować, ale...

0

oj panowie panowie
myslicie ze kod pisay w assemblerze jest duzo szybszy niz napisany w C

po pierwsze mnozenie przez 3 za pomoca shl i add jest wolnijsze niz mnozenie za pomoca funkcji mul , rzeczywiscie podobnych zabiegow uzywalo sie na porcesorach 486 no i na pentium tez sie oplacilo
ale przy prockach PII lub nowszych to juz nie

po drugie nie oplaca sie eraz piscac kodu w assemblerze tylko ze wzgledu na optymalizacje predkosci , bo trzeba tu rozpatrzyc naprawde duza liczbe warunkow ktore wplywaja na szybkosc dzialania wielopotokowosc, cache, mkrorozkazy itp
kompilatory radza sobie z tym duzo lepiej , zreszta sa tak pisane by generowly naprawde optymalny kod, jezeli napisalbys to w C to zapewniam was ze kod wygenerowany przez kompilator gdy ustawimy mu optymalizacje pod wzgledem predkosci nie bedzie gorszy ( a jak juz to minimalnie) a pewnie szybszy

jezeli nie wierzycie to karzcie kompilatorowi wyrzucic zrodla w assemblerze ktore generuje

0

mov edx, ecx
shl ecx, 1
add ecx, edx

to sa trzy cykle

a
mul ecx,3

wykonuje sie w jednym ( i to nie calym)

i te 3 rozkazy nie moga sie wykonac rownoczesnie bo operacje potrzebyja danych z poporzednich

no mze to sa 2 cykle bo shl ecx, 1 moze sie wykonac prawie rownoczesnie z mov edx, ecx , jezeli oczywiscie procesor posiada cos takiego jak mikroinstrukcje z których skladaja sie instrukcje

0

grees:

  1. Szybkość wynika nie ze zmiany mul na przesunięcia bitowe, ale z bezpośredniego dostępu do bitmapy w pamięci. Pomijamy w ten sposób dodatkowe operacje jakie muszą być przeprowadzone na obiekcie, których dokonuje kompilator, a dla nas są zbędne (np. Lock). Oczywiście kompilator mnożenie może szybciej zrobić, ale jeżeli już w asm piszę, to wolę mieć całość w asm.
  2. Od jakiego procka jest taka postać mul: mul ecx, 3 ? Chyba chodziło ci o IMUL.
  3. IMUL w takiej postaci w PII (nie wiem jak w nowszych) wykonuje się w ciągu 10 cykli i jest nieparowalną instrukcją. Instrukcja mov wykonuje się w ciągu 1 cyklu i jest parowalna w U i V. shl wykonuje się w jednym i jest parowalna w U i add także w 1 i jest parowalna w U i V. Jeżeli w nowszych prockach wykonuje się w ciągu 1 cykla, to pewnie masz rację, że jest szybsze. Ale dla wszystkich

1 użytkowników online, w tym zalogowanych: 0, gości: 1