Czcionka bitmapowa

Powrót do spisu treści

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 graficznej
OL036 - 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


obrazek

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.

obrazek

Kolor żółty jest kolorem tła, kolor zielony to kolor tuszu. Bitowo literka A zostanie zakodowana jako:

01111110
11111110
11000110
11000110
11111110
11000110
11000110
00000000

W 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.

obrazek

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:

Budowa pliku definiującego czcionkę bitmapową

Plik
definicji czcionki
Opis
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..
........
........
0
Na 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).

Odczyt pliku z dysku

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 miejsce
K03: 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 obszarze
K18     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

Rysowanie literek bitmapowych na powierzchni graficznej

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

Algorytm funkcji gfxDrawChar(screen,font,c,x,y,fcolor,bcolor)

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ęciach
K03: 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 graficznej
K09     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 bcolor
K17         Jeśli fcolor = -1, idź do K21 ; kolor przeźroczysty?
K18         Ustaw piksel wskazywany przez sp + j
        na fcolor i idź do K21
 
K19         Jeśli bcolor = -1, idź do K21 ; kolor przeźroczysty?
K20         Ustaw piksel wskazywany przez sp + j
        na bcolor
 
K21         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;
}

obrazek

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.

Algorytm funkcji gfxDrawText(screen,font,txt,x,y,fcolor,bcolor)

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  

Algorytm funkcji gfxTextLength(font,txt)

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;
}

obrazek


Podsumowanie


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2024 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

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