C dla każdego (cz. 6.)

Malowanie po ekranie

W dzisiejszej części zajmiemy się rysowaniem.

Wysokopoziomowe funkcje graficzne jako jednego z argumentów wymagają wskaźnika na RastPort (patrz "graphics/rastport.h"). Jest to struktura zawierająca informacje niezbędne do prac graficznych, jednak nas interesuje tylko część danych w niej zawartych. Każde otwarte okno ma własny RastPort. Jego adres można znaleˇć w polu "RPort" struktury "Window".

RastPort można traktować jako "czarną skrzynkę", potrzebną do rysowania. Jest ona dość wygodna. Używając różnych funkcji graficznych, podajemy współrzędne punktów względem lewego górnego rogu RastPortu. Podanie współrzędnych "wystających" poza RastPort nie powoduje żadnych problemów -- funkcje graficzne narysują tylko tyle, ile się zmieści. W RastPorcie zapisane jest również położenie kursora graficznego, tryb rysowania, aktualne kolory, wzór linii itp.

Położenie kursora zapisane jest w polach "cp_x" i "cp_y", po inicjalizacji RastPortu (w naszym wypadku otwarciu okna) kursor znajduje się w punkcie (0, 0), co zwykle wypada na górnej listwie okna. Po dodaniu w tagu "WA_Flags" flagi "WFLG_GIMMEZEROZERO" niedogodność ta znika, punkt (0, 0) wypada w lewym górnym rogu wnętrza okna, ale obsługa okien jest wolniejsza, a także większe jest zapotrzebowanie na pamięć, więc nie radzimy używać tego atrybutu. Przy opisywaniu gadżetów pokażemy, jak można się obejść bez pomocy GZZ.

Kolor tła jest odnotowany w polu "BgPen" (BPen), kolor "atramentu" w "FgPen" (APen). Nie wolno bezpośrednio (instrukcją przypisania) zmieniać kolorów, zapisanych w strukturze. Do tego celu przeznaczone są funkcje biblioteki "graphics.library":

void SetAPen( struct RastPort *rp, unsigned long pen );
void SetBPen( struct RastPort *rp, unsigned long pen );

Pierwsza z nich zmienia kolor atramentu, druga kolor tła. Obie funkcje wymagają jako argumentów wskaźnika na RastPort oraz numeru koloru.

Dane zawarte w polach "FgPen" i "BgPen" można było odczytywać. Zapisana tam wartość to numer koloru. Jednak od systemu 3.0 pojawiły się specjalne funkcje, zwracające numery kolorów jako wartość typu ULONG. Jest to przygotowane nieco na wyrost, ponieważ kości AGA mają ośmiobitowy blitter, czyli 256 rejestrów kolorów, w związku z czym do zapisania koloru wystarczy jeden bajt. Polecam jednak stosowanie tych funkcji, ponieważ przy jeszcze nowszych kościach może to być konieczne (przy założeniu, że będą jakieś "nowsze kości" -- miejmy nadzieję, że Escom nas nie zawiedzie).

ULONG GetAPen( struct RastPort *rp );
ULONG GetBPen( struct RastPort *rp );

Ponieważ nie wszystkie Amigi mają powyższe funkcje, należy sprawdzać wersję biblioteki (było to przerabiane w pierwszej części), możesz to również zobaczyć w listingu 9.: funkcje dajKolorA() i dajKolorB(). Jeżeli masz inkludy z systemu starszego niż 3.0, wystąpią problemy ze skompilowaniem tego listingu, ponieważ kompilator stwierdzi, że nie zadeklarowano niektórych funkcji. Sugeruję więc posiadaczom starszych inkludów zmodyfikować nieco program, usuwając wywołania nieznanych kompilatorowi funkcji, a następnie poszukać nowszych plików nagłówkowych.

Listing nr 9

Tak samo ma się sprawa z polem "DrawMode", w którym odnotowany jest sposób (tryb) rysowania po oknie. Do jego zmiany służy funkcja:

void SetDrMd( struct RastPort *rp, unsigned long drawMode );

Do jego odczytania zaś używamy:

ULONG GetDrMd( struct RastPort *rp );

Pojawiła się ona wraz z opisanymi wcześniej funkcjami GetAPen() i GetBPen() -- w wypadku starszych systemów należy po prostu odczytać pole RastPortu "DrawMode".

Są zdefiniowane następujące tryby rysowania:

JAM1: Tylko kolor atramentu jest umieszczany na RastPorcie.

JAM2: Zarówno kolory atramentu, jak i tła są umieszczane na RastPorcie. Tryb ten można odróżnić od JAM1 tylko w wypadku, gdy na RastPorcie umieszczane są napisy lub rysunki (o tym póˇniej).

COMPLEMENT: Jest wykonywana różnica symetryczna (XOR) obecnej zawartości RastPortu i tego, co ma być narysowane.

INVERSVID: Wykonywana jest inwersja kolorów.

Powyższe tryby można swobodnie mieszać ze sobą, jednak niektóre kombinacje nie mają sensu. Podejrzewam, że znaczenie poszczególnych trybów nie jest jeszcze zrozumiałe, myślę jednak, że się to nieco przejaśni po zapoznaniu się z działaniem przykładu. Do testowania sugeruję użyć rysowania napisów, ponieważ tylko w tym wypadku rysowany jest kolor tła, co pozwala odróżnić tryb JAM1 od JAM2.

Proponujemy teraz zajrzeć do przykładu:

Listing nr 9

Przykład ten to demonstracja użycia najprostszych funkcji graficznych systemu operacyjnego. Przy użyciu odpowiednich klawiszy (bądź klikając jednokrotnie na gadżecie zamykania okna) można się przełączać pomiędzy różnymi trybami pracy programu, takimi jak: rysowanie elips, punktów, zmiana kolorów itp. Rodzaj trybu, w jakim program znajduje się w danym momencie, jest wyświetlany na listwie tytułowej okna. Rysuje się za pomocą myszy, w sposób często niezbyt elegancki (można np. rysować po listwie tytułowej) i nieintuicyjny (nie jest tak od razu oczywiste, co w danym trybie robi przycisk lewy, a co prawy), ale ma to być tylko przykład, a nie jakiś użyteczny program. Gdyby chcieć zrobić to elegancko, to należałoby skorzystać z gadżetów, a na razie nie umiemy z nich korzystać. Szczegółowy opis listingu zamieszczony zostanie w następnym odcinku kursu.

Oto najczęściej powtarzające się w listach do mnie pytania:

1. Pytacie jak kompilować listingi. W kompilatorze SAS/C 6.x robi się to z Shella pisząc "SC nazwa_listingu.c LINK". Nie polecamy osobnego wywoływania linkera (slink), gdyż jest to bardziej skomplikowane.

2. W części 1, w listingu 3, w pętli "for" znajduje się instrukcja "licznik--". Powinno być oczywiście "licznik- -" (słownie: licznik minus minus).

3. W części 3, w listingu 5, zmienna "IntuitionBase" jest zmienną lokalną. Kompilator SAS/C skompiluje ten przykład bez zarzutu, ale chyba wszystkie inne kompilatory się "udławią" - zmienna ta powinna być globalna.