tłumaczył: Lizard/Aids
Tak, zgadza się, artykuł ten zawiera trochę informacji o szybkich procedurach graficznych trybu
wysokiej rozdzielczości. Lecz tym razem nie będzie
mowy o jednej tylko procedurze, a o całym ich zestawie niezbędnym do zrobienia dema: CLS, PLOT, DRAW
i CIRCLE! Jednym słowem, coś dla was koderzy!
Przed ujawnieniem szczegółów, pokażę wam, że
opisane tu procedury naprawdę działają i są bardzo
szybkie. Wyjdźcie na chwilę z magazynu i uruchomcie
program GREXAMPL.COM, poźniej wróćcie do tego
tekstu.
Zróbcie to teraz, a ja zaczekam!
Czekam........
Ok, już jesteście z powrotem? No to kontynuujemy...
Jeśli naprawdę obejrzeliście przykładowy program, to zobaczyliście okrąg, linię i punkt, a wszystko to w jednej ramce! Nawet duże obiekty (okrąg
o średnicy 200 pikseli, linia długości 256 pikseli) są
na tyle szybko rysowane, że można ich wyświetlić 50
w ciągu sekundy! (Demo nie używa procedury CLS -
wykorzystałem do pokazu tylko jeden ekran, gdyż
przy czyszczeniu ekranu zobaczylibyście nieprzyjemne miganie...)
W archiwum dołączonym do magazynu znajdują się
źródła (w formacie ATMAS II) przykładowego programu, który właśnie przed chwilą zobaczyliście.
Każdy plik źródłowy zawiera po jednej procedurze
do rysowania okręgu, linii oraz w głównym dwie
procedury: CLS i PLOT oraz wszystkie inne rzeczy
takie jak Display List, inicjowanie tablic, pętla główna,
itd... Zauważyć należy, że wszystkie procedury korzystają z tych samych tablic, tak więc, aby użyć np.
CIRCLE należy też skopiować podprogram inicjujący
z pliku MAIN.
Wszystkie procedury są całkiem szybkie, lecz jeśli
ktoś uważa, że nie są dość szybkie, to może je
jeszcze przyspieszyć. Jak? Należy tak rozlokować
procedury, tablice, zmienne, itp. w pamięci, by skoki
warunkowe nie odbywały się pomiędzy różnymi stronami, gdyż jak wiadomo, wykonują się wtedy o cykl
dłużej z powodu zmiany starszego bajtu PC... (Co, nie
wiedzieliście, że małe Atari ma w sobie PeCeta?)
Teraz trochę informacji na temat omawianych procedur...
Po pierwsze DRAW. Działa w pełnym zakresie od 0-255 na obydwu współrzędnych. Współrzędne początku i końca linii muszą być podane w zmiennych X0,
X1, Y0 i Y1.
Jeśli chcielibyście wiedzieć jak działa procka DRAW,
to zerknijcie do "Quick draw routine" w MegaZine #6.
Opublikowałem tam procedurę, która nie była jednak
najszybsza i używała całego dostępnego obszaru
na stronie zerowej... Później WosFilm przedstawił ulepszoną wersję. W końcu, kilka miesięcy temu (teraz
już lat - przyp. tł.) dostałem jeszcze jedną wersję
od Konopa z S.C.G. (dzięki!). Ta procka była całkiem
szybka, jednak mnie udało się napisać jeszcze szybszą, zawierającą najlepsze pomysły ze wszystkich
podprogramów. Ta (najszybsza) wersja jest zamieszczona w przykładzie na dysku.
Główne zmiany to: oddzielne procedury dla różnych
kierunków rysowania, pętla główna skopiowana jest
osiem razy (niektóre zmienne zmieniono na stałe,
które są różne w zależności od kopii procedury),
a sprawdzenie, czy nastąpił koniec linii umieszczony
jest tylko w jednej kopii (w pozostałych przypadek
ten nie występuje). Oczywiście procedura zmodyfikowana w ten sposób jest trochę długa (12 KB kodu
źródłowego), lecz kto by się tym przejmował...
Procedura PLOT powinna być wywoływana z pozycją X i Y w odpowiednich rejestrach. Prawdopodobnie
jest to najszybsza procedura; złożona z kilku innych.
Pomysły do niej zaczerpnąłem m.in. z Barymaga i od
Konopa/S.C.G.
Ponieważ procedura jest bardzo krótka, to nie ma
potrzeby używania czasochłonnych rozkazów JSR,
RTS. Dlatego też, PLOT napisany został jako makro.
Kolej na CLS. Procedura wzorowana jest na artykule Detail'a "Fast graphics operations for the 6502"
z MegaZine'u #7. Celem jest przyspieszenie czyszczenia pamięci ekranu poprzez wpisanie do pamięci
większej ilości rozkazów STA (oszczędność czasu
poprzez wyeliminowanie pętli). Problem w tym, że
chcąc wyczyścić 7kB (tyle ma pamięć ekranu) tylko
przez samo STA, potrzeba 21 KB na tę "procedurę"!
To zbyt wiele. Postanowiłem więc połączyć tę metodę
z tradycyjną pętlą - 2.5 KB STA wykonane osiem razy.
Czas zużywany przez pętle jest znikomy, a wymagania
pamięciowe nie zmuszają do kupna PeCeta. Główna
procedura (2.5 B długości) tworzona jest przez mały
generator...
OKRĘGI
Ostatnia procedua - CIRCLE - napisana jest całkowicie przeze mnie. Nie wiem, czy ktoś inny też ją
napisał; przynajmniej nic mi o tym nie wiadomo...
Parametry dla tej procy należy przekazać w rejestrach (X, Y, R odpowiednio w X, Y, A). Współrzędne
X, Y mogą mieć dowolny zakres (0-255), a promień
ograniczony jest w zakresie od 0 do 127 (pamiętać
należy, że średnica jest dwa razy dłuższa).
Wyjaśnię teraz jak działa ten podprogram. Zazwyczaj rysując okrąg, stosuje się funkcje trygonometryczne. Od razu wam radzę: zapomnijcie o nich - są zbyt wolne!!!
Cudowną rzeczą czyniącą mój okrąg szybkim jest
to (twierdzenie Pitagorasa):
/|
c/ |b
/ |
-----
a c^2=a^2+b^2
Zacznijmy od początku... Podczas rysowania
okręgu określonego przez (X, Y, R), dodajemy do siebie współrzędne X, Y przed zapaleniem każdego pi-
ksela. Pozwala to tylko na rysowanie okręgu o parametrach (0, 0, R). Dzielimy też okrąg na osiem części:
...
. " | " .
: \ 3 | 2 / :
: \ | / :
.' 4 \ | / 1 '.
:-------+-------:
'. 5 / | \ 8 .'
: / | \ :
: / 6 | 7 \ :
'. | .'
""""""
(sorry za ten paskudny rysunek, lecz w proporcji
ciężko jest zrobić dobre ASCII, jakby ktoś nie wiedzał, to jest to kółko podzielone na osiem równych
części - przyp. tł.)
Można rysować wszystkie osiem części w tym samym czasie. Wykonując obliczenia dla pierwszej
części zapalamy piksele o współrzędnych: (X,Y) (Y,X)
(-Y,X) (-X,Y) (-X,-Y) (-Y,-X) (Y,-X) (X,-Y)
Teraz pozostało już wyjaśnić jak obliczyć tę pieprzoną "pierwszą część" okręgu. Ok... Wiadomo, że
każdy punkt na okręgu jest równo oddalony od środka - to jest promień. Jeśli środkiem jest punkt
(0, 0), to prawdą jest, że: X^2 + Y^2 = R^2. Teraz
definiujemy funkcję F(X,Y) = X^2+Y^2-R^2. Wartością
tejże funkcji jest pewien rodzaj wskazania, gdzie
położony jest piksel: wartość dodatnia - poza okręgiem, ujemna - wewnątrz okręgu.
Reszta jest już dość podobna do procedury DRAW
opisanej w MegaZine #6. Zaczynamy od X=R i Y=0. Ponieważ będziemy rysować tylko pierwszą część (0-45
stopni), wystarczy, że będziemy wykonywać tylko
pionowe (w górę) lub ukośne (w górę i lewo) kroki.
Obliczamy wartość F(X-0.5,Y+1) - brzeg pomiędzy
dwoma możliwym pikselami - i sprawdzamy znak wyniku. Ujemny - pionowy krok, dodatni - ukośny.
A jak wyglądają obliczenia? Na szczęście wszystkie
"^2" zniknęły, więc można użyć prostych rozkazów
assemblera. Na początek obliczamy:
F(R-0.5,0) = 0.25-R
A może myślicie, że powinno być F(R-0.5,1)... Oczywiście macie rację, lecz to nie pomyłka. Program
(zajrzyjcie do archiwum) rozpoczyna się w środku
procedury dla pionowego kroku, umieszczając piksel
na pozycji (R, 0), czyli krok ten wykonywany jest
przed pierwszym "prawdziwym" krokiem...
Ponieważ dodawane i odejmowane są tylko wartości
całkowite, można zrobić to prościej - wszystkie znaki
(+/-) podczas obliczeń będą identyczne. Tak więc,
w programie znajduje się:
F(R-0.5,0) = -R
Następne wartości będą obliczone na podstawie
poprzednich w następujący sposób (używając nowych
współrzędnych X, Y):
Po pionowym kroku:
F(X-0.5,Y+1) = F(X-0.5,Y) + 2*Y+1
I po skośnym kroku:
F(X-0.5,Y+1) = F(X+0.5,Y) + 2*Y-2*X+1
I to wszystko. Jeśli chcecie, możecie używać moich
procedur we własnych demach i innych produkcjach,
lecz, proszę, nie zapomnijcie wspomnieć, kto jest ich
autorem. Jeśli ktoś przyspieszy moje procedury,
niech da mi znać!
Udanego kodowania!!!!
Bewesoft