Wymagane jest zapoznanie się z następującymi podrozdziałami:
P019 - Pierwszy program dla Windows
OL031 - instalacja biblioteki SDL w środowisku Dev-C++
OL032 - inicjalizacja biblioteki SDL
OL033 - powierzchnie graficzne w SDL
OL034 - przygotowanie plików własnej biblioteki graficznej
OL035 - rysowanie po powierzchni graficznejOL036 - algorytm Bresenhama rysowania odcinka
OL037 - obcinanie grafiki do prostokąta
OL038 - podstawowe algorytmy wypełniania obszarów
OL039 - algorytm Smitha
OL040 - praca w środowisku sterowanym zdarzeniami
UWAGA!!!Bieżące opracowanie NIE JEST KURSEM programowania biblioteki SDL. Są to materiały przeznaczone do zajęć na kole informatycznym w I LO w Tarnowie.
Przed pracą z tym rozdziałem utwórz w środowisku Dev-C++ nowy projekt SDL i dołącz do niego pliki SDL_gfx.cpp oraz SDL_gfx.h. Jeśli nie przeczytałeś zalecanych rozdziałów, koniecznie zrób to, a wszystko stanie się jasne. W przeciwnym razie odpuść sobie również ten rozdział. Opis funkcji bibliotecznych znajdziesz w podsumowaniu SDL006.
Artykuł nie jest już rozwijany
Tworząc grafikę komputerową bardzo szybko staniemy przed problemem umieszczenia tekstu na powierzchni graficznej. Może to być jakiś tytuł, opis czy wartość liczbowa (np. na osiach tworzonego wykresu funkcji). Biblioteka SDL nie udostępnia funkcji rysowania tekstu. Jednakże zadanie nie jest wcale trudne (o ile nie chcemy osiągnąć jakości czcionek oferowanych przez profesjonalne systemy graficzne - Windows, Linux czy MacOS). Do rysowania prostych literek wykorzystamy funkcje naszej biblioteki SDL_gfx, które stworzyliśmy na poprzednich zajęciach.
Pierwszym sposobem rysowania na ekranie graficznym literek jest zastosowanie map bitowych. Najprościej mówiąc, mapa bitowa jest małą powierzchnią graficzną, gdzie piksele reprezentowane są przez pojedyncze bity. Ponieważ bit może przyjmować tylko dwa różne stany - 0 i 1 - kolory pikseli również ograniczone są do dwóch - kolor tła (bit 0) oraz kolor tuszu (bit 1). Każda literka posiada swoją własną mapę bitową.
Mapa bitowa literki składa się z n kolumn i m wierszy. Będziemy ją nazywali matrycą znaku. Poniżej mamy matrycę 8 x 8 z literką A.

Kolor żółty jest kolorem tła, kolor zielony to kolor tuszu. Bitowo literka A zostanie zakodowana jako:
01111110
11111110
11000110
11000110
11111110
11000110
11000110
00000000W tym przypadku jeden wiersz matrycy znaku zawiera 8 bitów, zatem mieści się w 1 bajcie pamięci. Cały znak wymaga 8 bajtów danych. Jeśli przyjmiemy inny rozmiar matrycy (np. 10 x 16), to każdy wiersz będzie wymagał 10 bitów, zatem zostanie umieszczony w dwóch bajtach - starsze 6 bitów nie będzie wykorzystywane. Jeden znak będzie wymagał 32 bajty danych.

Większa matryca pozwala lepiej przedstawić kształt literki, lecz znaki są oczywiście większe.
Gdy już wiemy jak będą reprezentowane w pamięci komputera poszczególne literki alfabetu, określmy niezbędną strukturę danych gfxFont.
typedef struct { Uint32 type; Uint32 w, h; Uint8 * cp[256]; Uint32 cw[256]; } gfxFont;Poszczególne pola posiadają następujące znaczenia:
type - jeśli zawiera 0, to czcionka jest bitmapowa. Jeśli zawiera 1, to czcionka jest wektorowa. w - określa szerokość matrycy znaku w bajtach. Ilość bitów w wierszu matrycy jest indywidualna dla każdego znaku (czcionka proporcjonalna) i podajemy ją w tablicy cw[ ]. h - określa liczbę wierszy matrycy, czyli jej wysokość. Parametr ten można wykorzystywać przy druku wielowierszowym. cp[ ] - tablica 256 wskaźników bitmap. Bitmapę wybieramy kodem ASCII znaku, który jest indeksem. Jeśli wskaźnik danego znaku ma wartość NULL, to znak nie posiada bitmapy. cw[ ] - tablica szerokości poszczególnych znaków. Indeks jest kodem ASCII znaku. Szerokość podana jest w bitach i określa faktyczną szerokość matrycy danego znaku. Wykorzystana będzie przy rysowaniu kolejnych znaków tekstu. Kolejny problem - skąd weźmiemy definicje znaków? Cóż, zaprojektujemy je sami. Aby nie komplikować zbytnio zadania, wykorzystamy narzędzia ogólnie dostępne w każdym systemie operacyjnym - zwykły notatnik. Przy jego pomocy utworzymy plik tekstowy opisujący kształty poszczególnych literek. Plik zapiszemy na dysku pod odpowiednią nazwą w katalogu projektu. Następnie napiszemy funkcję biblioteczną gfxOpenFont(), która odczyta zawartość pliku, zinterpretuje ją i na tej podstawie utworzy strukturę gfxFont, którą dalej będziemy wykorzystywali w funkcji rysującej teksty na ekranie graficznym. Brzmi sensownie, zatem do dzieła:
Plik
definicji czcionkiOpis B 1 10 65 8 ........ .XXXXX.. XXXXXXX. XX...XX. XX...XX. XXXXXXX. XX...XX. XX...XX. ........ ........ 66 8 ........ XXXXXX.. XX...XX. XX...XX. XXXXXX.. XX...XX. XX...XX. XXXXXX.. ........ ........ 0Na początku pliku, w pierwszym wierszu, podajemy rodzaj czcionki. Możliwe są dwa warianty: B - czcionka bitmapowa
V - czcionka wektorowa, którą opiszemy w następnym rozdziale. Właściwie może być tutaj dowolny znak różny od B.Dalej mamy dwie liczby całkowite. Pierwsza liczba oznacza szerokość matrycy znaku w bajtach - w przykładzie jest to liczba 1. Druga liczba określa wysokość matrycy znaku - w przykładzie 10. W dalszej kolejności umieszczamy definicje poszczególnych literek.
Definicja każdej literki rozpoczyna się od jej kodu ASCII oraz szerokości w bitach. Kolejność definiowania liter jest dowolna.
Poszczególne bity kodujemy dwoma znakami: . - bit 0, czyli tło oraz X - bit 1, czyli tusz. Taki sposób gwarantuje wystarczającą czytelność definicji dla człowieka oraz uwalnia nas od konieczności posiadania programu edytora czcionek - w sumie edytor możesz sobie napisać później sam i przeprogramować cały ten system definiowania znaków wg własnego uznania.Bardzo ważne jest, aby rzeczywista szerokość matrycy znaku zdefiniowana przez . i X odpowiadała szerokości określonej za literką. W przeciwnym razie dane mogą zostać źle odczytane.
W pliku umieszczasz tyle definicji znaków, ile jest ci potrzebne w aplikacji. Najlepiej rób to alfabetycznie. Można przykładowo zdefiniować czcionkę, która zawiera tylko same cyfry, albo same duże literki alfabetu.
Plik kończy się kodem 0. Nasz przykład definiuje plik z dwoma literami A i B.
Definicje mogą się powtarzać - w takim przypadku w strukturze gfxFont zostanie zapisana ostatnia z nich. Możesz wykorzystać tę cechę do chwilowej zmiany kształtu jakiejś literki bez straty oryginału (np. przy testowaniu) - po prostu na końcu pliku tekstowego dopisujesz nową definicję.
Tutaj masz kilka plików czcionek bitmapowych utworzonych w matrycy 8 x 8 punktów:
bmpnorm8x8.fnt - pismo normalne bmpbold8x8.fnt - pismo pogrubione bmpprop8x8.fnt - pismo proporcjonalne bmppropbold8x8.fnt - pismo pogrubione proporcjonalne Możesz na ich podstawie spróbować zaprojektować inne pliki czcionek, lecz jest to dosyć pracochłonne zajęcie. Czcionka niekoniecznie musi odwzorowywać litery alfabetu - mogą to być dowolne symbole, np. figury szachowe, kadry animowanej postaci, symbole stosowane w elektronice, itp. Pliki zapisz w katalogu projektu SDL pod ich oryginalnymi nazwami (ważne!!!, bo inaczej nie będą ci działały przykłady z dalszej części lekcji).
Gdy mamy przygotowany plik z definicjami matryc czcionek, musimy go odpowiednio odczytać i utworzyć na jego podstawie strukturę gfxFont. W tym celu napiszemy funkcję
gfxOpenFont() pracującą wg poniższego algorytmu:Wejście
file - plik tekstowy zawierający definicję matryc liter Wyjście
font - zainicjowana struktura gfxFont Zmienne pomocnicze
i,j - zmienne sterujące pętli iteracyjnych mw - szerokość matrycy znakowej w bajtach mh - wysokość matrycy znakowej w bajtach cw - szerokość znaku w bitach cp - wskaźnik wierszy matrycy code - przechowuje kod przetwarzanego znaku lbuf - przechowuje przetwarzany wiersz matrycy znaku tbuf - przechowuje wiersz tekstu opisującego wiersz matrycy znaków literkami . i X Lista kroków
K01: Otwórz file do odczytu ; przygotowujemy plik do odczytu danych K02: Jeśli w pierwszym wierszu pliku file jest napis
B, to idź do K03.
Inaczej wykonaj część algorytmu dla czcionki wektorowej.; sprawdzamy rodzaj pliku. Jeśli mamy czcionkę wektorową, to wykonamy
; algorytm opisany w kolejnej lekcji. Na tym etapie zarezerwujemy dla
; niego puste miejsceK03: Utwórz pustą strukturę font ; rezerwujemy pamięć dla struktury gfxFont K04: Odczytaj z file mw i mh i umieść je w:
font.w ← mw oraz font.h ← mh; odczytujemy dwie pierwsze dane z pliku - szerokość matrycy w bajtach
; oraz jej wysokośćK05 Utwórz lbuf o rozmiarze mw bajtów ; rezerwujemy pamięć na wiersz matrycy znaku K06 Utwórz tbuf o rozmiarze mw x 8 + 1 bajtów ; rezerwujemy pamięć na tekstowy opis wiersza matrycy znaków K07 Odczytaj liczbę z file do code ; najpierw odczytujemy kod znaku do zmiennej code K08 Jeśli code = 0, idź do kroku K19 ; kończymy przetwarzanie, gdy kod zero K09 Odczytaj liczbę z file do cw i umieść w:
font.cw[code] ← cw; następnie odczytujemy szerokość znaku do zmiennej cw i umieszczamy ją w
; tablicy font.cw[ ]K10 jeśli font.cp[code] = NULL, to zarezerwuj obszar pamięci
o rozmiarze mw x mh i umieść jego adres
w cp oraz w font.cp[code]; Jeśli struktura nie zawiera jeszcze definicji znaku, to w pamięci rezerwujemy
; obszar na matrycę znaku i jego adres umieszczamy
; we wskaźniku cp oraz w tablicy wskaźników font.cp[code]K11 Dla i = 0,1,...,mh - 1: wykonuj kroki K12...K17 ; przetwarzamy kolejne wiersze definicji matrycy znakowej K12 Odczytaj wiersz tekstu z file do tbuf ; odczytujemy wiersz definicji zawierający znaki X i . K13 Wyzeruj lbuf K14 Dla j = 0,1,...,cw - 1: wykonuj kroki K15...K16 ; przeglądamy kolejne znaki wiersza tekstowego K15 Przesuń wszystkie bity lbuf o 1 w lewo ; w lbuf robimy miejsce na nowy bit K16 Jeśli tbuf[j] = 'X', ustaw najmłodszy bit lbuf na 1
inaczej wyzeruj najmłodszy bit lbuf; ustawiamy lub zerujemy najmłodszy bit lbuf w zależności od tego,
; czy w definicji jest znak X, czy znak .K17 Całą zawartość lbuf przepisz pod adres cp
modyfikując wskaźnik cp z każdym przesłanym bajtem; gotowy wiersz matrycy umieszczamy w zarezerwowanym obszarze pamięci
; po każdym wierszu wskaźnik cp wskazuje wolne miejsce w obszarzeK18 Wróć do kroku K07 ; kontynuujemy pętlę K19 Zamknij file ; kończymy pracę z plikiem K20 Zwróć font i zakończ algorytm ; zwracamy wynik i kończymy
Na podstawie przedstawionego algorytmu napiszemy dwie funkcje:
gfxOpenFont(nazwa_pliku) - odczytuje z pliku definicje matryc liter i na ich podstawie tworzy strukturę gfxFont
gfxCloseFont(gfxFont) - zwalnia pamięć zarezerwowaną przy tworzeniu struktury gfxFont.Na początku pliku SDL_gfx.cpp dopisz czerwony tekst:
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne 2007 // // Biblioteka procedur graficznych dla trybów 32 bitowych //------------------------------------------------------- #include <SDL/SDL_gfx.h> #include <stack> #include <fstream> using namespace std;Na końcu pliku SDL_gfx.cpp wpisz definicje funkcji:
// gfxOpenFont odczytuje z pliku dyskowego definicje czcionki bitmapowej // lub wektorowej i na ich podstawie tworzy strukturę gfxFont // W przypadku błędu zwraca NULL. Inaczej zwraca adres zainicjowanej // struktury gfxFont. // filename - nazwa pliku z definicją czcionki //------------------------------------------------------------------ gfxFont * gfxOpenFont(const char * filename) { ifstream file(filename); int i, j, k; int mw; // szerokość matrycy w bajtach int mh; // liczba wierszy w matrycy int cw; // szerokość definicji znaku w bitach int code; // kod znaku Uint8 * cp; Uint8 * lbuf; // bufor wiersza matrycy char * tbuf; // bufor wiersza tekstu char tstr[2]; // do odczytu rodzaju czcionki if(file.fail()) return NULL; // kończymy przy błędzie dyskowym gfxFont * font = new gfxFont; // tworzymy pustą strukturę gfxFont for(i = 0; i < 256; i++) { font->cp[i] = NULL; font->cw[i] = 0; } font->type = 0; file >> tstr; //odczytujemy rodzaj pliku - B=bitmap, inne=vector if(tstr[0] == 'B') // sprawdzamy rodzaj czcionki { // *** tutaj obsługujemy czcionkę bitmapową *** file >> mw >> mh; // odczytujemy z pliku szerokość mw font->w = mw; // i wysokość mh. Odczytane liczby font->h = mh; // umieszczamy w strukturze font lbuf = new Uint8[mw]; tbuf = new char[(mw << 3) + 1]; while(true) { file >> code; if(!code) break; // kończymy, gdy kod jest równy zero file >> cw; // odczytujemy szerokość matrycy w bitach font->cw[code] = cw; // zapamiętujemy ją w strukturze font if(!font->cp[code]) font->cp[code] = cp = new Uint8[mw * mh]; for(i = 0; i < mh; i++) // przetwarzamy tekst na bity { file >> tbuf; for(j = 0; j < mw; j++) lbuf[j] = 0; for(j = 0; j < cw; j++) { Uint16 temp = 0; for(k = 0; k < mw; k++) { temp |= lbuf[k] << 1; lbuf[k] = (Uint8) temp; temp >>= 8; } if(tbuf[j] == 'X') lbuf[0] |= 1; } for(j = 0; j < mw; j++) *(cp++) = lbuf[j]; } } delete [] lbuf; // zwalniamy pamięć delete [] tbuf; } else { //*** tutaj obsługujemy czcionkę wektorową *** } file.close(); // zamykamy plik return font; } // Usuwa czcionkę bitmapową z pamięci // font - wskaźnik struktury gfxFont //------------------------------------------------------------------ void gfxCloseFont(gfxFont * font) { if(font) { for(int i = 0; i < 256; i++) if(font->cp[i]) delete [] font->cp[i]; delete font; } }Na początku pliku nagłówkowego SDL_gfx.h wpisz czerwony tekst
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne 2007 // // Biblioteka procedur graficznych dla trybów 32 bitowych //------------------------------------------------------- #include <SDL/SDL.h> typedef struct { Uint32 type; Uint32 w, h; Uint8 * cp[256]; Uint32 cw[256]; } gfxFont; char * gfxLibVersion(void);Na końcu pliku nagłówkowego SDL_gfx.h dopisz:
gfxFont * gfxOpenFont(const char * filename); void gfxCloseFont(gfxFont * font);Parametry funkcji gfxOpenFont() i gfxFCloseFONTont() są następujące:
filename - nazwa pliku tekstowego zawierającego definicje matryc liter czcionki font - wskaźnik struktury gfxFont
Przedostatnim problemem do rozwiązania jest opracowanie algorytmu rysowania literek, których definicje przechowuje struktura gfxFont. Problem rozwiążemy za pomocą dwóch funkcji:
gfxDrawChar() - rysuje jedną literkę
gfxDrawText() - rysuje ciąg znaków
Wejście
screen - wskaźnik struktury SDL_Surface font - wskaźnik zainicjowanej struktury gfxFont c - znak do narysowania x,y - współrzędne lewego górnego piksela matrycy znaku fcolor - kolor tuszu. Jeśli równy -1, to kolor przeźroczysty. bcolor - kolor tła. Jeśli równy -1, to kolor przeźroczysty. Wyjście
Narysowanie znaku c na pozycji x,y powierzchni graficznej. Ponieważ funkcja jest bardzo prosta, cały znak powinien się mieścić w obszarze graficznym ekranu. Funkcja zwraca szerokość narysowanej literki, którą można wykorzystać do odpowiedniej zmiany współrzędnej x dla następnego znaku.
Zmienne pomocnicze
i,j,k - zmienne sterujące pętli iteracyjnych sp - wskaźnik pikseli na ekranie graficznym mp - wskaźnik bajtu w matrycy znaku h - wysokość matrycy w - szerokość matrycy w pikselach msk - maska wyodrębniania bitu z bajtu matrycy mbyte - przechowuje przetwarzany bajt z matrycy Lista kroków
K01: w ← 0 ; zerujemy wstępnie szerokość matrycy K02 Jeśli font→type = 0, idź do kroku K03.
Inaczej wykonaj część algorytmu dla
czcionki wektorowej; sprawdzamy rodzaj czcionki. Jeśli jest to czcionka wektorowa, to na tym etapie rezerwujemy
; jedynie miejsce dla algorytmu rysowania czcionek wektorowych, który omówimy
; na następnych zajęciachK03: mp ← font→cp[c] ; adres matrycy znaku umieszczamy we wskaźniku mp K04: Jeśli mp = NULL, idź do kroku K24 ; jeśli znak nie posiada matrycy, kończymy K05 sp ← pozycja piksela x,y ; wyliczamy pozycję lewego górnego narożnika matrycy K06 h ← font→h ; odczytujemy wysokość matrycy K07 w ← font→cw[c] ; odczytujemy szerokość matrycy dla znaku c K08 Powtarzaj h razy kroki K09...K23 ; w pętli przetwarzamy kolejne wiersze matrycy znaku w piksele
; na powierzchni graficznejK09 k ← 0 ; k jest indeksem bajtu w wierszu matrycy K10 msk ← 0 ; msk jest maską bitową do wyodrębniania bitów z matrycy K11 Dla j = w-1, w-2,...,0: wykonuj K12...K21 ; przetwarzamy poszczególne bity wiersza matrycy K12 Jeśli msk > 0, idź do kroku K16 ; sprawdzamy, czy został przetworzony cały bajt matrycy K13 msk ← 1 ; jeśli tak, ustawiamy nową maskę K14 mbyte ← bajt z adresu mp + k ; oraz pobieramy kolejny bajt z matrycy znaku c K15 k ← k + 1 ; zwiększamy indeks bajtu w wierszu K16 Jeśli mbyte & msk = 0, idź do K19 ; w zależności od stanu bitu piksel wskazywany przez wskaźnik
; sp + j kolorujemy na fcolor lub bcolorK17 Jeśli fcolor = -1, idź do K21 ; kolor przeźroczysty? K18 Ustaw piksel wskazywany przez sp + j
na fcolor i idź do K21K19 Jeśli bcolor = -1, idź do K21 ; kolor przeźroczysty? K20 Ustaw piksel wskazywany przez sp + j
na bcolorK21 Przesuń bity msk o 1 w lewo ; w masce ustawiamy kolejny bit do testu K22 mp ← mp + font→w ; przesuwamy wskaźnik mp na kolejny wiersz matrycy K23 sp ← sp + screen→w ; wskaźnik pikseli przenosimy do następnego wiersza ekranu K24: Zwróć w ; zwracamy szerokość znaku w pikselach K25 Zakończ Na podstawie powyższego algorytmu piszemy funkcję gfxDrawChar(). Dopisz do końca pliku SDL_gfx.cpp poniższy kod funkcji:
// Rysuje pojedynczy znak na powierzchni graficznej // screen - wskaźnik struktury SDL_Surface // font - wskaźnik struktury gfxFont // c - znak do narysowania // x,y - współrzędne lewego górnego punktu znaku // fcolor - kolor tuszu znaku // bcolor - kolor tła znaku //------------------------------------------------------------------ Uint32 gfxDrawChar(SDL_Surface * screen, gfxFont * font, unsigned char c, Sint32 x, Sint32 y, Sint32 fcolor, Sint32 bcolor) { Uint32 h, w = 0, k; if(!(font->type)) { // *** wykonujemy część algorytmu dla czcionki bitmapowej *** Uint32 * sp; // wskaźnik pozycji na ekranie Uint8 * mp; // wskaźnik pozycji matrycy znakowej Uint8 msk, mbyte; mp = font->cp[c]; if(mp) { sp = (Uint32 *)(screen->pixels) + y * screen->w + x; h = font->h; w = font->cw[c]; for(int i = 0; i < h; i++) { k = msk = 0; for(int j = w - 1; j >= 0; j--) { if(!msk) { msk = 1; mbyte = mp[k++]; } if(mbyte & msk) { if(fcolor != -1) sp[j] = fcolor; } else { if(bcolor != -1) sp[j] = bcolor; } msk <<= 1; } mp += font->w; // następny wiersz matrycy sp += screen->w; // przechodzimy do następnej linii ekranu } } } else { // *** wykonujemy część algorytmu dla czcionki wektorowej *** } return w; }A na końcu pliku SDL_gfx.h dopisz prototyp tej funkcji:
Uint32 gfxDrawChar(SDL_Surface * screen, gfxFont * font, unsigned char c, Sint32 x, Sint32 y, Sint32 fcolor, Sint32 bcolor);Parametry funkcji gfxDrawChar() są następujące:
screen - wskaźnik struktury SDL_Surface font - wskaźnik struktury gfxFont c - znak do narysowania x, y - współrzędne lewego górnego rogu matrycy znaku fcolor - kolor tuszu - literki bcolor - kolor tła Poniżej przedstawiamy prosty program testowy, który odczytuje czcionkę z pliku bmpbold8x8.fnt i wyświetla w sposób przypadkowy literki tej czcionki. Przypominamy, plik czcionki należy umieścić w katalogu projektu SDL, albo w wywołaniu funkcji gfxOpenFont() należy podać ścieżkę dostępu do pliku. Inaczej program nie będzie działał. Plik czcionki jest do pobrania na początku tego artykułu.
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne // // P025 - wyświetlanie liter bitmapowych //-------------------------------------- #include <SDL/SDL_gfx.h> #include <time.h> const int SCRX = 320; // stałe określające szerokość i wysokość const int SCRY = 240; // ekranu w pikselach int main(int argc, char * argv[]) { if(SDL_Init(SDL_INIT_VIDEO)) exit(-1); atexit(SDL_Quit); SDL_Surface * screen; if(!(screen = SDL_SetVideoMode(SCRX, SCRY, 32, SDL_HWSURFACE))) exit(-1); gfxFont * bf = gfxOpenFont("bmpbold8x8.fnt"); // inicjujemy generator liczb pseudolosowych srand((unsigned)time(NULL)); // rozpoczynamy pętlę wyświetlania literek Sint32 x,y; Uint32 fc,bc; char c; SDL_Event event; bool running = true; do { if(SDL_MUSTLOCK(screen)) if(SDL_LockSurface(screen) < 0) exit(-1); // obliczamy współrzędne literki, znak oraz kolor tekstu (tło czarne) x = 8 * (rand() % (screen->w >> 3)); y = 8 * (rand() % (screen->h >> 3)); c = 32 + rand() % 96; fc = ((rand() % 256) << 16) | ((rand() % 256) << 8) | (rand() % 256); // wyświetlamy literkę gfxDrawChar(screen, bf, c, x, y , fc, 0); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); // odświeżamy ekran - tylko obszar literki SDL_UpdateRect(screen, x, y, 8, 8); // w pętli obsługi zdarzeń reagujemy tylko na klawisz ESC i zamknięcie okna while(SDL_PollEvent(&event)) switch(event.type) { case SDL_KEYDOWN: if(event.key.keysym.sym != SDLK_ESCAPE) break; case SDL_QUIT: running = false; break; } }while(running); // Zwalniamy pamięć zajętą przez strukturę czcionki gfxCloseFont(bf); return 0; }

Pozostaje ostatni, prosty już problem - wypisywanie tekstu za pomocą czcionki bitmapowej. Rozwiążemy go wykorzystując funkcję rysowania pojedynczego znaku gfxDrawChar(), którą będziemy po prostu wywoływać dla kolejnych literek tekstu. Poniżej przedstawiamy algorytm funkcji gfxDrawText(). Tekst przekazywany do funkcji musi być, zgodnie z konwencją stosowaną w C++, zakończony znakiem o kodzie 0. Dodatkowo napiszemy jeszcze jedną funkcję gfxTextLength(), która oblicza szerokość tekstu na podstawie jego definicji w strukturze gfxFont. Funkcja taka bardzo przydaje się przy centrowaniu tekstu i dosuwaniu go do lewej krawędzi wybranego obszaru powierzchni graficznej.
Wejście
screen - wskaźnik struktury SDL_Surface font - wskaźnik zainicjowanej struktury gfxFont txt - wskaźnik ciągu znaków zakończonych kodem 0 x,y - współrzędne lewego górnego piksela pierwszej matrycy znaku fcolor - kolor tuszu bcolor - kolor tła Wyjście
Narysowanie tekstu txt na pozycji x,y powierzchni graficznej. Ponieważ funkcja jest bardzo prosta, cały tekst powinien się mieścić w obszarze graficznym ekranu.
Zmienne pomocnicze
c - znak tekstu wskazywanego przez txt Lista kroków
K01: Odczytaj znak c spod adresu txt K02 txt ← txt + 1 ; przesuwamy wskaźnik na następny znak tekstu K03: Jeśli c = 0, idź do kroku K07 ; znak o kodzie 0 kończy tekst K04: Narysuj c na pozycji x,y K05 Zwiększ x o szerokość matrycy c K06 Wróć do kroku K01 ; kontynuujemy pętlę K07 Zwróć x i zakończ
Wejście
font - wskaźnik zainicjowanej struktury gfxFont txt - wskaźnik ciągu znaków zakończonych kodem 0 Wyjście
Zwraca sumę len szerokości matryc znaków literek zawartych w tekście txt.
Zmienne pomocnicze
c - znak tekstu wskazywanego przez txt Lista kroków
K01: len ← 0 ; zerujemy szerokość tekstu K02 Odczytaj znak c wskazywany przez txt ; przechodzimy przez kolejne znaki w tekście K03: txt ← txt + 1 K04: Jeśli c = 0, idź do kroku K07 ; koniec, wynik w len K05 len ← len + font→cw[c] ; dodając ich szerokości do szerokości tekstu K06 Wróć do kroku K02 K07 Zwróć len i zakończ ; koniec, wynik w len Na podstawie podanych algorytmów piszemy kod funkcji gfxDrawText() oraz gfxTextLength(). Na końcu pliku SDL_gfx.cpp dopisz poniższy tekst:
// Rysuje tekst bitmapowy na powierzchni graficznej // screen - wskaźnik struktury SDL_Surface // font - wskaźnik struktury gfxFont // txt - wskaźnik tekstu zakończonego kodem 0 // x,y - współrzędne lewego górnego narożnika tekstu // fcolor - kolor tuszu znaku // bcolor - kolor tła znaku //------------------------------------------------------------------ Sint32 gfxDrawText(SDL_Surface * screen, gfxFont * font, char * txt, Sint32 x, Sint32 y, Sint32 fcolor, Sint32 bcolor) { unsigned char c; while(c = * (txt++)) x += gfxDrawChar(screen, font, c, x, y, fcolor, bcolor); return x; } // Oblicza szerokość tekstu bitmapowego // font - wskaźnik struktury gfxFont // txt - wskaźnik tekstu zakończonego kodem 0 //------------------------------------------------------------------ Sint32 gfxTextLength(gfxFont * font, char * txt) { Sint32 len = 0; unsigned char c; while(c = * (txt++))len += font->cw[c]; return len; }Do pliku nagłówkowego SDL_gfx.h dopisz prototypy funkcji:
Sint32 gfxDrawText(SDL_Surface * screen, gfxFont * font, char * txt, Sint32 x, Sint32 y, Sint32 fcolor, Sint32 bcolor); Sint32 gfxTextLength(gfxFont * font, char * txt);Poniżej mamy program tekstowy, który wypisuje na ekranie graficznym nazwę naszej szkoły oraz nazwę koła informatycznego. Program zamykamy klawiszem ESC lub kliknięciem w ikonę zamykania aplikacji, która znajduje się na pasku tytułowym.
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne // // P026 - wyświetlanie tekstu bitmapowego //--------------------------------------- #include <SDL/SDL_gfx.h> const int SCRX = 320; // stałe określające szerokość i wysokość const int SCRY = 240; // ekranu w pikselach int main(int argc, char * argv[]) { if(SDL_Init(SDL_INIT_VIDEO)) exit(-1); atexit(SDL_Quit); SDL_Surface * screen; if(!(screen = SDL_SetVideoMode(SCRX, SCRY, 32, SDL_HWSURFACE))) exit(-1); // odczytujemy z pliku definicje czcionki bitmapowej gfxFont * bf = gfxOpenFont("bmpbold8x8.fnt"); if(SDL_MUSTLOCK(screen)) if(SDL_LockSurface(screen) < 0) exit(-1); // Rysujemy 1000 losowych linii for(int i = 0; i < 1000; i++) gfxLine(screen, rand() % screen->w, rand() % screen->h, rand() % screen->w, rand() % screen->h, ((rand() % 256) << 16) | ((rand() % 256) << 8) | (rand() % 256)); // x i y wskazują środek ekranu Sint32 x = screen->w >> 1; Sint32 y = screen->h >> 1; // obliczamy długość najdłuższego tekstu char * tx1 = "I LICEUM OGÓLNOKSZTAŁCĄCE"; char * tx2 = "im. Kazimierza Brodzińskiego"; char * tx3 = "w Tarnowie"; char * tx4 = "Koło Informatyczne 2007"; Sint32 len = gfxTextLength(bf, tx2); // Rysujemy wypełnioną ramkę obejmującą tekst z marginesem 6 pikseli SDL_Rect r; r.x = x - (len >> 1) - 6; r.y = y - (bf->h << 1) - 6; r.w = len + 12; r.h = (bf->h << 2) + 12; SDL_FillRect(screen, &r, 0xff0000); r.x += 3; r.y += 3; r.w -= 6; r.h -= 6; SDL_FillRect(screen, &r, 0x800000); // W ramce umieszczamy napisy gfxDrawText(screen, bf, tx1, x - (gfxTextLength(bf, tx1) >> 1), y - (bf->h << 1), 0xffffff, -1); gfxDrawText(screen, bf, tx2, x - (gfxTextLength(bf, tx2) >> 1), y - bf->h, 0xffffff, -1); gfxDrawText(screen, bf, tx3, x - (gfxTextLength(bf, tx3) >> 1), y, 0xffff00, -1); gfxDrawText(screen, bf, tx4, x - (gfxTextLength(bf, tx4) >> 1), y + bf->h, 0x00ffff, -1); if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); // odświeżamy ekran SDL_UpdateRect(screen, 0, 0, 0, 0); bool running = true; SDL_Event event; // czekamy na zakończenie działania programu while(running && SDL_WaitEvent(&event)) { switch(event.type) { case SDL_KEYDOWN: if(event.key.keysym.sym != SDLK_ESCAPE) break; case SDL_QUIT: running = false; break; } } // zamykamy czcionkę gfxCloseFont(bf); return 0; }

![]() | I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe