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
OL041 - czcionka bitmapowa
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 SDL007.
Artykuł nie jest już rozwijany
Na poprzednich zajęciach utworzyliśmy prosty system rysowania czcionek bitmapowych, czyli takich, które przechowują kształt poszczególnych literek w matrycach bitmapowych. Czcionki bitmapowe doskonale nadają się do rysowania literek w ich naturalnej wielkości. Sytuacja się jednak pogarsza przy skalowaniu literek. Wtedy uwidaczniają się piksele zawarte w matrycach - czcionka po prostu wygląda nieładnie. Oczywiście można przygotować zestawy znaków bitmapowych obejmujące różne wielkości literek, np. matryce 8x8, 8x10, 9x12, 10x16 itd. To dużo pracy.
Na dzisiejszej lekcji wprowadzimy drugi rodzaj czcionek - czcionki wektorowe. Czcionki wektorowe można bez utraty jakości pwiększać. Nowe kroje pisma będą korzystały z tych samych funkcji co czcionki bitmapowe - po prostu dopiszemy w funkcjach obsługę krojów wektorowych. Dzięki temu rozwiązaniu program nie musi rozróżniać rodzaju czcionki - dokonywane to będzie automatycznie w funkcjach obsługi krojów pisma.
Na początek omówimy sposób rysowania literek wektorowych. Literki będą rysowane na prostokątnej siatce:
W obrębie siatki rysujemy znak za pomocą ciągu łamanych:
W powyższym przykładzie mamy dwie łamane, które zaznaczono dwoma różnymi kolorami kreski (w rzeczywistym znaku będzie to oczywiście jeden kolor). Łamane zbudowane są z ciągu odcinków połączonych końcami. Zatem łamaną można zdefiniować jako ciąg punktów wyznaczających kolejne wierzchołki. Dla narysowanej powyżej literki A będą to punkty:
łamana niebieska : (1,8) - (1,3) - (3,1) - (5,1) - (7,3) - (7,8) łamana zielona : (1,6) - (7,6) Łamane można w prosty sposób zdefiniować podając liczbę tworzących je punktów wierzchołkowych oraz kolejne współrzędne tych punktów. Definicje łamanych mogą występować jedna po drugiej. Ciąg ten będzie zakończony liczbą 0 - żadna łamana nie może przecież składać się z 0 wierzchołków. Według tej zasady przykładowa literka A będzie reprezentowana przez ciąg liczb:
6 1 8 1 3 3 1 5 1 7 3 7 8 2 1 6 7 6 0
Profesjonalne czcionki (np. Windows True Type) do opisu kształtu znaków wykorzystują krzywe wektorowe Beziera pozwalające na stosowanie różnych łuków. Dzięki temu czcionki nie posiadają kanciastych kształtów. Pamiętaj jednakże, iż nasz system jest systemem uproszczonym (nawet bardzo uproszczonym) i zrozumienie zasad jego działania nie wymaga doktoratu z grafiki komputerowej. Na nasze potrzeby zupełnie wystarczy, a zdolna osoba może uzyskiwać całkiem interesujące efekty!
W pamięci czcionka wektorowa będzie reprezentowana przez znaną nam z poprzedniej lekcji strukturę gfxFont.
typedef struct { Uint32 type; Uint32 w, h; Uint8 * cp[256]; Uint32 cw[256]; } gfxFont;Dla czcionki wektorowej poszczególne pola tej struktury 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ść siatki wektorowej h - określa wysokość siatki wektorowej cp[ ] - tablica 256 wskaźników definicji poszczególnych znaków. Definicja jest ciągiem łamanych zakończonym kodem 0. cw[ ] - tablica szerokości poszczególnych znaków. Indeks jest kodem ASCII znaku. Szerokość podana jest w pikselach i określa faktyczną szerokość danego znaku. Różnice w stosunku do wersji bitmapowej obejmują pole w, które dla czcionki wektorowej wyraża maksymalną szerokość siatki wykorzystywanej do rysowania znaku oraz tablicę wskaźników cp[], które dla czcionki wektorowej wskazują ciągi łamanych tworzących znak a nie matryce bitmapowe.
Plik
definicji czcionkiOpis V 8 12 65 8 6 0 9 0 4 2 2 3 2 5 4 5 9 2 0 7 5 7 0 66 8 10 0 2 0 9 4 9 5 8 5 6 4 5 5 4 5 3 4 2 0 2 2 0 5 4 5 0 67 8 8 5 4 3 2 2 2 0 4 0 7 2 9 3 9 5 7 0 0Powyższy plik definiuje trzy literki:
A - kod 65
B - kod 66
C - kod 67
Na początku pliku, w pierwszym wierszu podajemy rodzaj czcionki. Możliwe są dwa warianty: B - czcionka bitmapowa
V - czcionka wektorowa, właściwie może tutaj być dowolna litera, różna od B.W drugim wierszu są dwie liczby całkowite. Pierwsza z nich określa maksymalną szerokość siatki, na której rysowane są poszczególne literki. Druga liczba określa maksymalną wysokość tej siatki.
W następnych wierszach znajdują się definicje znaków. Każda definicja rozpoczyna się od dwóch liczb:
- kodu ASCII literki
- rzeczywistej szerokości znakuJeśli kod ASCII jest równy 0, to oznacza to koniec definicji. Literki nie muszą być definiowane alfabetycznie (chociaż to jest zalecane) i definicje mogą się powtarzać - w takim przypadku w strukturze gfxFont będzie umieszczona ostatnia definicja. Pozwala to na testowanie nowych kształtów literek bez utraty starych,
Definicja kształtu jest ciągiem łamanych zakończonym liczbą 0. Każda łamana rozpoczyna się od liczby informującej o liczbie wierzchołków łamanej, następnie występuje odpowiednia liczba współrzędnych x i y tych wierzchołków. Definicje łamanych można podawać w jednym lub w kilku wierszach - nie ma to większego znaczenia - dla czytelności proponuję jednak każdą łamaną umieszczać w osobnym wierszu. Zakres współrzędnych jest ograniczony od 0 do 255.
Plik czcionki wektorowej można przygotować w notatniku, jednakże jest to trudniejsze niż w przypadku pliku czcionki bitmapowej, ponieważ nie widzimy kształtów literek. Przydałby się tutaj program edytora czcionek - być może coś takiego zaprogramujemy.
Do odczytania pliku czcionki wektorowej wykorzystamy funkcję gfxOpenFont(). Tworząc ją na poprzednich zajęciach zarezerwowaliśmy w niej miejsce dla fragmentu odczytującego czcionkę wektorową. Algorytm jest następujący:
Wejście
file - plik tekstowy zawierający definicję czcionki wektorowej - plik jest już otwarty i pierwszy znak odczytany Wyjście
font - zainicjowana struktura gfxFont Zmienne pomocnicze
code - przechowuje kod przetwarzanego znaku q - lista przechowująca dane odczytane z pliku plen - przechowuje liczbę wierzchołków łamanej x,y - współrzędne wierzchołka łamanej Lista kroków
K01 font→type ← 1 ; ustawiamy rodzaj struktury na czcionkę wektorową K02: Odczytaj z file do font→w i font→h ; odczytujemy z pliku szerokość i wysokość siatki K03: Odczytaj z file do code ; czytamy kod znaku K04: Jeśli code = 0, zakończ ; koniec pliku, kończymy K05: Usuń zawartość font→cp[code] ; jeśli znak był już definiowany, usuwamy jego poprzednią definicję K06 Odczytaj z file do font→cw[code] ; odczytujemy szerokość znaku K07 Odczytaj z file do plen ; czytamy liczbę wierzchołków łamanej K08 Dodaj plen na koniec q ; umieszczamy ją w kolejce K09 Jeśli plen = 0, idź do K14 ; przerywamy pętlę przy końcu definicji znaku K10 Powtórz plen razy kroki K11...K12 K11 Odczytaj z file do x i y ; odczytujemy współrzędne wierzchołka K12 Dodaj x i y na koniec q ; i umieszczamy je na liście K13 Idź do K07 ; kontynuujemy pętlę odczytu łamanych K14 Zarezerwuj w font→cp[code] pamięć
o rozmiarze q; miejsce na definicję znaku K15 Przepisz q do font→cp[code] ; definicję przepisujemy do struktury font K16 Wyzeruj q ; czyścimy listę K17 Idź do K03 ; kontynuuje pętlę odczytu definicji znaków 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> #include <list> using namespace std;Znajdź funkcję gfxOpenFont() i wyszukaj w niej pokazany na czerwono fragment :
delete [] lbuf; // zwalniamy pamięć delete [] tbuf; } else { //*** tutaj obsługujemy czcionkę wektorową *** } file.close(); // zamykamy plik return font; }Poniższy tekst wpisz pomiędzy czerwone klamerki w funkcji gfxOpenFont().
list<Uint8>q; // lista wierzchołków łamanych int plen, x, y; font->type = 1; // typ - czcionka wektorowa file >> (font->w) >> (font->h); while(true) // pętla odczytu definicji znaków { file >> code; if(!code) break; if(font->cp[code]) { delete [] font->cp[code]; // usuwamy poprzedni wpis font->cp[code] = NULL; } file >> (font->cw[code]); while(true) // pętla odczytu łamanych { file >> plen; q.push_back(plen); if(!plen) break; for(i = 0; i < plen; i++) // pętla odczytu wierzchołków { file >> x >> y; q.push_back(x); q.push_back(y); } } cp = font->cp[code] = new Uint8[q.size()]; for(list<Uint8>::iterator z = q.begin(); z != q.end(); z++) * (cp++) = * z; q.clear(); }
Poniżej umieściliśmy algorytm fragmentu funkcji gfxDrawChar(), który dotyczy omawianych tutaj liter wektorowych:
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 narożnika siatki znaku fcolor - kolor tuszu. bcolor - kolor tła, jeśli równy -1, to nie będzie użyty - tło przezroczyste 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
pp - wskaźnik listy zawierającej definicje łamanych plen - przechowuje liczbę wierzchołków dla łamanej xp,yp - współrzędne wierzchołka łamanej w - zawiera szerokość narysowanego znaku Lista kroków
K01 pp ←font→cp[c] ; w pp ustawiamy adres początku listy zawierającej definicje łamanych K02: Jeśli pp = NULL, zakończ ; jeśli nie ma definicji łamanych, kończymy K03: w ← font→cw[c] ; do w wstawiamy szerokość znaku K04: Jeśli bcolor = -1, idź do K06 ; jeśli tło przezroczyste, nie rysujemy go K05 Narysuj wypełniony prostokąt
w kolorze bcolor na współrzędnych
x,y i o wymiarach:
szerokość = w, wysokość = font→h; rysujemy prostokąt w kolorze tła pokrywający siatkę znaku K06 Odczytaj plen z adresu pp ; odczytujemy liczbę wierzchołków w łamanej K07 pp ← pp + 1 ; przesuwamy adres na następny element listy K08 Jeśli plen = 0, zakończ ; jeśli koniec listy, kończymy przetwarzanie znaku K09 Odczytaj xp z adresu pp ; pobieramy współrzędną x wierzchołka K10 pp ← pp + 1 ; następny element listy K11 Odczytaj yp z adresu pp ; pobieramy współrzędną y wierzchołka K12 pp ← pp + 1 K13 Ustaw początek linii na x+xp, y+yp K14 Narysuj punkt x+xp, y+yp w kolorze fcolor ; rysujemy punkt początkowy K15 plen ← plen - 1 ; pobraliśmy jeden wierzchołek, zmniejszamy stan licznika K16 Dopóki plen > 0, wykonuj kroki K17...K22 ; w pętli pobieramy kolejne wierzchołki łamanej i rysujemy do nich linie w kolorze fcolor K17 Odczytaj xp z adresu pp K18 pp ← pp + 1 K19 Odczytaj yp z adresu pp K20 pp ← pp + 1 K21 Rysuj linię do x+xp,y+yp w kolorze fcolor K22 plen ← plen - 1 K23 Zakończ Na podstawie powyższego algorytmu uzupełnimy funkcję gfxDrawChar() o obsługę czcionek wektorowych. W tym celu odszukaj w pliku SDL_gfx.cpp poniższy fragment funkcji gfxDrawChar():
} else { // *** wykonujemy część algorytmu dla czcionki wektorowej *** } return w; }Pomiędzy czerwone klamerki wpisz następujący kod:
Uint8 * pp = font->cp[c]; Uint32 plen, xp, yp; if(pp) { w = font->cw[c]; if(bcolor != -1) // rysujemy tło znaku { SDL_Rect r; r.x = x; r.y = y; r.w = w; r.h = font->h; SDL_FillRect(screen, &r, bcolor); } while(plen = * (pp++)) { xp = x + * (pp++); yp = y + * (pp++); gfxMoveTo(xp, yp); gfxPlot(screen, xp, yp, fcolor); while(--plen) { xp = * (pp++); yp = * (pp++); gfxLineTo(screen, x + xp, y + yp, fcolor); } } }Do przetestowania funkcji rysowania czcionek wektorowych przygotowaliśmy prosty program testowy, który odczytuje definicję czcionki z pliku vecprop9x12.fnt (skopiuj ten plik do swojego katalogu projektowego) i wyświetla wszystkie zdefiniowane w nim litery. Program kończy się po naciśnięciu klawisza ESC lub przycisku zamykania aplikacji.
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne // // P027 - wyświetlanie tekstu wektorowego //--------------------------------------- #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 wektorowej gfxFont * font = gfxOpenFont("vecprop9x12.fnt"); if(SDL_MUSTLOCK(screen)) if(SDL_LockSurface(screen) < 0) exit(-1); // Wyświetlamy napis: char * t = "Przykładowa czcionka wektorowa"; Sint32 x = screen->w >> 1; Sint32 y = 0; gfxDrawText(screen, font, t, x - (gfxTextLength(font, t) >> 1), y, 0xffff00, -1); // po kolei wyświetlamy wszystkie literki czcionki x = font->w; y = 3 * font->h; for(int i = 32; i < 256; i++) { if(gfxDrawChar(screen, font, i, x, y, 0xffffff, -1)) x += 2 * font->w; if(x >= screen->w - 2 * font->w) { x = font->w; y += 2 * font->h; } } t = "(C)2007 Koło Informatyczne I-LO"; gfxDrawText(screen, font, t, screen->w - gfxTextLength(font, t), screen->h - font->h, 0xff0000,-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(font); 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