Opis biblioteki newgfx
 

Grafika SLD - biblioteka newgfx

Grafika komputerowa ma olbrzymie zastosowanie w informatycznie - praktycznie całe współczesne oprogramowanie użytkowe pracuje w trybie graficznym. Naukę programowania grafiki rozpoczniemy od biblioteki SDL (ang. Simple Directmedia Library), która jest darmowo dostępna w Internecie. Biblioteka ta udostępnia środowisko graficzne dla programów. Jednakże nie posiada zdefiniowanych funkcji do tworzenia grafiki na poziomie pikseli. Dlatego bibliotekę SDL uzupełnimy opracowaną w naszej szkole biblioteką newgfx.

 

Instalacja bibliotek SDL i newgfx dla Code::Blocks

Dla celów zajęć na kole informatycznym przygotowałem następującą procedurę instalacji:

 

Ściągnij na swój dysk archiwum SDL z naszego serwera i rozpakuj je. Wewnątrz znajdziesz katalog SDL. Otwórz go. Zawartość będzie następująca:

 

Katalogi include i lib skopiuj do katalogu MinGW wewnątrz instalacji CodeBlocks.

 

Plik biblioteki dynamicznej SDL.dll skopiuj do katalogu Windows/System32. Jeśli katalog ten jest dla ciebie niedostępny, to plik SDL.dll musi być w tym samym katalogu co program exe korzystający z biblioteki SDL. W przypadku Code::Blocks jest to katalog bin/Debug lub bin/Release wewnątrz katalogu projektowego.

 

Uruchom Code::Blocks, wybierz Create a new project, w okienku dialogowym zaznacz kategorię jako Win32 GUI Project i dalej postępuj standardowo. Na koniec w edytorze powinieneś mieć szablon programu okienkowego Windows - nie przejmuj się tym, gdyż zaraz zastąpimy go czymś innym.

 

Do katalogu projektowego skopiuj z archiwum SDL 2 pliki newgfx (jeden jest programem w C++ z kodem biblioteki, drugi jest plikiem nagłówkowym).

 

Wybierz z menu opcję Project/Build options. W oknie dialogowym kliknij w zakładkę Linker settings i przekopiuj do prawego pola Other linker options poniższy tekst:

 

-lmingw32
-mwindows
-lSDLmain
-lSDL

 

Wybierz z menu opcję Project/Add files i za jego pomocą dodaj pliki newgfx.h oraz newgfx.cpp.

 

Usuń z edytora tekst programu Windows i wklej poniższy kod, który stanie się szablonem programu SDL:

 

// Plik szablonowy SDL
// (C)2011 Koło Informatyczne
// I LO w Tarnowie
//-------------------------------

#include "newgfx.h"

int main(int argc, char *argv[])
{

  if(!SDL_Init(SDL_INIT_VIDEO))
  {
    atexit(SDL_Quit);

    SDL_Surface * screen = SDL_SetVideoMode(320, 240, 32, SDL_HWSURFACE);


    if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);

    // tutaj wstawiamy program graficzny

    for(Sint32 y = 0; y < screen->h; y++)
      for(Sint32 x = 0; x < screen->w; x++)
        if((x % 10) && (y % 10)) gfxPlot(screen,x,y,0xff0000);
        else                     gfxPlot(screen,x,y,0x00ffff);

    if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);

    SDL_UpdateRect(screen, 0, 0, 0, 0);

    // oczekujemy klawisza ESC

    int waiting = 0;

    do
    {
      SDL_Event event;

      while (SDL_PollEvent(&event))
        if ((event.type == SDL_QUIT) ||
           ((event.type == SDL_KEYDOWN) &&
            (event.key.keysym.sym == SDLK_ESCAPE))) waiting = 1;
    } while(!waiting);
  }
  return 0;
}

 

Skompiluj i uruchom program. Jeśli nie popełniłeś żadnego błędu, to dostaniesz poniższe okienko z motywem kraty:

 

 

Na koniec z menu wybierz opcję File/Save project as template i w okienku dialogowym podaj nazwę dla szablonu SDL. Od tej pory dostęp do projektu SDL będziesz miał poprzez opcję Create a new project/User templates.

 

Analiza programu

Program szablonowy został tak zaprojektowany, aby umożliwiał tworzenie prostej, statycznej grafiki w SDL. Poniżej krótko opisujemy jego podstawowe bloki funkcjonalne.

#include "newgfx.h"
Plik nagłówkowy newgfx.h daje nam dostęp do wszystkich funkcji SLD oraz do procedur graficznych biblioteki newgfx.
int main(int argc, char *argv[])
Funkcja główna musi posiadać dokładnie taką postać, chociaż nie korzystamy z jej argumentów.
if(!SDL_Init(SDL_INIT_VIDEO))
Na początku programu wywołujemy funkcję inicjalizacji biblioteki SDL. Jako parametr przekazuje się, które z jej podsystemów mają być inicjowane. Tutaj inicjujemy podsystem wideo. Wszystkie możliwości, to:
SDL_INIT_VIDEO  - inicjalizacja podsystemu obrazu
SDL_INIT_AUDIO  - inicjalizacja podsystemu dźwięku
SDL_INIT_CDROM  - inicjalizacja podsystemu obsługi CDROM
SDL_INIT_JOYSTICK  - inicjalizacja podsystemu obsługi joysticka
SDL_INIT_TIMER  - inicjalizacja podsystemu liczników czasu
SDL_INIT_EVERYTHING  - inicjalizacja wszystkich wymienionych powyżej podsystemów
SDL_INIT_NOPARACHUTE  - zapobiega przechwytywaniu przez SDL sygnałów błędów fatalnych
SDL_INIT_EVENTTHREAD  - inicjalizacja podsystemu obsługi wątków
atexit(SDL_Quit);  
Tutaj ustawiamy funkcję, która zostanie wywołana przy zakończeniu programu. SDL_Quit wykonuje sprząta po bibliotece SDL.
SDL_Surface * screen = SDL_SetVideoMode(320,240,32,SDL_HWSURFACE);
SDL_Surface to najważniejsza struktura SDL. Zawiera ona informacje o powierzchni graficznej, po której program rysuje.
typedef struct
{
  Uint32 flags;
  SDL_PixelFormat * format;
  int w, h;
  Uint16 pitch;
  void * pixels;
  SDL_Rect clip_rect;
  int refcount;
} SDL_Surface;

Powierzchnię rysunkową inicjujemy za pomocą funkcji SDL_SetVideoMode().  Kolejne parametry tej funkcji określają rozmiary okna w poziomie i pionie, liczbę bitów na piksel oraz parametry powierzchni graficznej.

if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen);

...

if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
Pomiędzy wywołaniami tych funkcji umieszczamy nasz program graficzny, który dokonuje rysowania. Ich zadaniem jest sprawdzenie, czy powierzchnia rysunkowa musi być zablokowana dla innych wątków. Jeśli tak, to ją blokują przed rysowaniem i zwalniają po rysowaniu.
SDL_UpdateRect(screen, 0, 0, 0, 0);
Wywołanie tej funkcji powoduje przeniesienie zmian w powierzchni graficznej na ekran.
    // oczekujemy klawisza ESC

    int waiting = 0;

    do
    {
      SDL_Event event;

      while (SDL_PollEvent(&event))
        if ((event.type == SDL_QUIT) ||
           ((event.type == SDL_KEYDOWN) &&
            (event.key.keysym.sym == SDLK_ESCAPE))) waiting = 1;
    } while(!waiting);
  }
  return 0;
}
Ta część programu wykorzystuje system obsługi zdarzeń w SDL do czekania aż użytkownik naciśnie klawisz ESC. Wtedy program się kończy - w dalszej części kursu, gdy nasze umiejętności wzrosną, będziemy mogli inaczej obsługiwać zdarzenia.

 

Rysowanie punktów

Rysowanie rozpoczniemy od stawiania punktów graficznych, czyli pikseli. Wszystkie podane tutaj programy wstawiamy do naszego szablonu pod komentarzem. Biblioteka newgfx posiada funkcję zaprojektowaną dokładnie w tym celu (nazwy wszystkich funkcji rozpoczynają się od gfx):

 

gfxPlot(s,x,y,c);

s - wskaźnik do struktury SDL_Surface, która określa parametry powierzchni graficznej
x - współrzędna x w oknie graficznym
y - współrzędna y w oknie graficznym (uwaga, oś OY jest skierowana w dół - punkt 0,0 leży w lewym górnym narożniku okna)
c - kolor punktu

 

Pierwsze trzy parametry są w miarę proste do zrozumienia. Opisu wymaga ostatni parametr c - kolor. Każdy piksel jest reprezentowany na powierzchni graficznej przez 32 bity, czyli 4 bajty. Najstarszy bajt określa przeźroczystość powierzchni - nie używamy go. Trzy kolejne bajty określają tzw. składowe koloru:

 

R - czerwoną
G - zieloną
B - niebieską

 

Jeśli masz pod ręką lupę, to oglądnij przez nią powierzchnię ekranu monitora. Zobaczysz, że obraz zbudowany jest z siatki punktów czerwonych, zielonych i niebieskich.

 

 

Kolory pośrednie otrzymujemy regulując natężenie świecenia kolorów podstawowych:

 

 

W kodzie piksela każdy z kolorów podstawowych zajmuje pole o szerokości 8 bitów. Z tego powodu kolory podstawowe mogą przyjmować intensywności od 0 do 255. Wartość 0 oznacza brak świecenia, 255 - świecenie z maksymalną intensywnością. Do określania kolorów najlepiej nadaje się system szesnastkowy - każda składowa jest wtedy zapisywana przy pomocy dwóch cyfr szesnastkowych. Poniżej są typowe wartości:

 

00 - brak
40 - 1/4 intensywności
80 - 1/2 intensywności
C0 - 3/4 intensywności
FF - pełna intensywność

 

W szablonie programu usuń kod spod komentarza pomiędzy instrukcjami if(SDL_MUSTLOCK... i wpisz poniższy tekst, po czym skompiluj i uruchom program:

 

  gfxPlot(screen,10,10,0xff0000); // piksel czerwony
  gfxPlot(screen,20,20,0x00ff00); // piksel zielony
  gfxPlot(screen,30,30,0x0000ff); // piksel niebieski

 

Efektem działania powyższego programu są trzy punkty w kolorach czerwonym, zielonym i niebieskim:

 

 

Kolory pośrednie otrzymamy mieszając barwy podstawowe. Do poprzedniego kodu dopisz:

 

  gfxPlot(screen,40,40,0xffff00); // piksel żółty
  gfxPlot(screen,50,50,0xff00ff); // piksel buraczkowy
  gfxPlot(screen,60,60,0xffffff); // piksel biały

 

 

Aktualną szerokość powierzchni graficznej odczytujemy ze struktury SDL_Surface. Wskaźnik do tej struktury w naszym programie nazywa się screen.

 

screen->w - szerokość w pikselach

screen->h - wysokość w pikselach

 

Poprawne współrzędne punktów na powierzchni graficznej leżą w przedziałach:

 

x → 0 ... screen->w - 1

y → 0 ... screen->h - 1

 

Kolejny kod wypełnia całe okno graficzne jednolitym kolorem czerwonym (zastąp nim poprzedni kod):

 

Sint32 x,y;

for(y = 0; y < screen->h; y++)
  for(x = 0; x < screen->w; x++)
    gfxPlot(screen,x,y,0xff0000);

 

Typ Sint32 w bibliotece SDL oznacza daną 32 bitową ze znakiem. Dane 32 bitowa bez znaku posiada typ Uint32 - typy te wprowadzono, aby zapewnić przenośność programów na inne platformy sprzętowe.

 

 

Dla ćwiczenia uzyskaj wypełnienie zielone, niebieskie, żółte, białe. Poeksperymentuj z różnymi wartościami dla składowych kolorów.

 

Ciekawy efekt kolorystyczny uzyskamy przez uzależnienie koloru piksela od jego pozycji na powierzchni graficznej. Zaczniemy od koloru niebieskiego, który będzie się zmieniał w osi OX:

dla x = 0  składowa niebieska ma wartość 0

dla x = screen->w - 1 składowa niebieska ma wartość 255

Układamy proporcję:

 

 

Z proporcji wyznaczamy wartość składowej niebieskiej b:

 

 

Wpisz poniższy kod (stary kod usuń!):

 

Sint32 x,y;
Uint32 b;

for(y = 0; y < screen->h; y++)
  for(x = 0; x < screen->w; x++)
  {
    b = (255 * x) / (screen->w - 1); // składowa niebieska

    gfxPlot(screen,x,y,b);
  }

 

 

Składową zieloną g uzależnimy od położenia pionowego y:

 

 

Aby wstawić ją do kodu koloru piksela, musimy wykonać przesunięcie o 8 bitów w lewo - wtedy bity wartości składowej zielonej znajdą się na właściwej pozycji.  Uzupełnij kod programu:

 

Sint32 x,y;
Uint32 b,g;

for(y = 0; y < screen->h; y++)
  for(x = 0; x < screen->w; x++)
  {
    b = (255 * x) / (screen->w - 1); // składowa niebieska
    g = (255 * y) / (screen->h - 1); // składowa zielona

    gfxPlot(screen,x,y,b | (g << 8));
  }

 

 

Składową czerwoną uzależnimy od iloczynu współrzędnych x i y:

 

 

Składową czerwoną należy w kodzie koloru przesunąć o 16 bitów w lewo.

 

Sint32 x,y;
Uint32 b,g,r;

for(y = 0; y < screen->h; y++)
  for(x = 0; x < screen->w; x++)
  {
    b = (255 * x) / (screen->w - 1);                   // składowa niebieska
    g = (255 * y) / (screen->h - 1);                   // składowa zielona
    r = (255 * x * y) / (screen->w-1) / (screen->h-1); // składowa czerwona

    gfxPlot(screen,x,y,b | (g << 8) | (r << 16);
  }

 

 

W lewym górnym narożniku wszystkie trzy składowe mają wartość 0 - kolor czarny.
W prawym górnym narożniku składowa niebieska ma wartość 255, pozostałe składowe mają wartości 0, kolor wynikowy niebieski.
W lewym dolnym narożniku składowa zielona ma wartość 255, pozostałe mają wartości 0, kolor wynikowy zielony.
W prawym dolnym narożniku wszystkie trzy składowe otrzymują wartość 255, kolorem wynikowym zostaje kolor biały.
W innych miejscach okienka składowe mieszają się ze sobą i otrzymujemy barwy pośrednie.

 

Ciekawy efekt kolorystyczny uzyskamy, odejmując składową czerwoną r od 255 - wtedy wartości się odwrócą i maksimum barwy czerwonej będzie w lewym górnym narożniku, czyli tam, gdzie pozostałe dwie składowe, zielona i niebieska, mają wartość 0:

 

Sint32 x,y;
Uint32 b,g,r;

for(y = 0; y < screen->h; y++)
  for(x = 0; x < screen->w; x++)
  {
    b = (255 * x) / (screen->w - 1);                         // składowa niebieska
    g = (255 * y) / (screen->h - 1);                         // składowa zielona
    r = 255 - (255 * x * y) / (screen->w-1) / (screen->h-1); // składowa czerwona

    gfxPlot(screen,x,y,b | (g << 8) | (r << 16);
  }

 

 

Rysowanie linii

Biblioteka newgfx posiada kilka funkcji do rysowania linii.

 

gfxHLine(s,x,y,c,len) - rysuje linię poziomą od strony lewej do prawej
s - wskaźnik do struktury SDL_Surface
x,y - punkt początkowy linii
c - kolor linii
len - długość linii

 

gfxVLine(s,x,y,c,len) - rysuje linię pionową od góry w dół
s - wskaźnik do struktury SDL_Surface
x,y - punkt początkowy linii
c - kolor linii
len - długość linii

Uwaga: dla tych wersji funkcji rysowane linie muszą w całości mieścić się na powierzchni graficznej. W przeciwnym razie program zostanie zatrzymany z błędem dostępu do pamięci.

 

Uruchom poniższy program:

 

  gfxHLine(screen,0,screen->h/2,0xff0000,screen->w/2);
  gfxHLine(screen,screen->w/2,screen->h/2,0x00ff00,screen->w/2);
  gfxVLine(screen,screen->w/2,0,0x0000ff,screen->h/2);
  gfxVLine(screen,screen->w/2,screen->h/2,0xffff00,screen->h/2);

 

 

Kolejna funkcja rysuje dowolną linię pomiędzy dwoma punktami o podanych współrzędnych - w tej wersji linia w całości musi się mieścić na powierzchni graficznej:

gfxLine(s,x1,y1,x2,y2,c)
s - wskaźnik do struktury SDL_Surface
x1,y1 - współrzędne punktu startowego
x2,y2 - współrzędne punktu końcowego
c - kolor linii

 

Dopisz do poprzedniego kodu:

 

  gfxLine(screen,0,0,screen->w-1,screen->h-1,0xff00ff);
  gfxLine(screen,0,screen->h-1,screen->w-1,0,0x00ffff);

 

 

Łamana jest krzywą zbudowaną z odcinków połączonych końcami. Poniższe dwie funkcje przydają się przy rysowaniu łamanych:

gfxMoveTo(x,y) - ustawia punkt początkowy łamanej
x,y - współrzędne początku łamanej

 

gfxLineTo(s,x,y,c) - rysuje linie od punktu ustawionego przez gfxMoveTo() lub punkt końcowy poprzedniego wywołania gfxLineTo() do punktu o współrzędnych x,y. Linia w całości musi się mieścić na powierzchni graficznej.
s - wskaźnik do struktury SDL_Surface
x,y - punkt końcowy linii
c - kolor

 

Dopisz do poprzedniego programu poniższy kod:

 

  gfxMoveTo(0,screen->h/2);
  gfxLineTo(screen,screen->w/2,0,0xffffff);
  gfxLineTo(screen,screen->w-1,screen->h/2,0xffffff);
  gfxLineTo(screen,screen->w/2,screen->h-1,0xffffff);
  gfxLineTo(screen,0,screen->h/2,0xffffff);

 

 

Podsumowanie:

gfxPlot(s,x,y,c) - rysuje punkt w kolorze c na współrzędnych x,y.

s - wskaźnik do struktury SDL_Surface
x,y - współrzędne punktu
c - kolor punktu

 

gfxHLine(s,x,y,c,len) - rysuje linię poziomą od strony lewej do prawej, poczynając od punktu x,y.
s - wskaźnik do struktury SDL_Surface
x,y - punkt początkowy linii
c - kolor linii
len - długość linii

 

gfxVLine(s,x,y,c,len) - rysuje linię pionową od góry w dół, poczynając od punktu x,y.
s - wskaźnik do struktury SDL_Surface
x,y - punkt początkowy linii
c - kolor linii
len - długość linii

 

gfxLine(s,x1,y1,x2,y2,c) - rysuje linię od x1,y1 do x2,y2.
s - wskaźnik do struktury SDL_Surface
x1,y1 - współrzędne punktu startowego
x2,y2 - współrzędne punktu końcowego
c - kolor linii

 

gfxMoveTo(x,y) - ustawia punkt początkowy łamanej na pozycji x,y
x,y - współrzędne początku łamanej

 

gfxLineTo(s,x,y,c) - rysuje linie do punktu x,y. Punkt początkowy jest określany przez gfxMoveTo() lub poprzednią funkcję gfxLineTo().
s - wskaźnik do struktury SDL_Surface
x,y - punkt końcowy linii
c - kolor

 



List do administratora Serwisu Edukacyjnego Nauczycieli I LO

Twój email: (jeśli chcesz otrzymać odpowiedź)
Temat:
Uwaga: ← tutaj wpisz wyraz  ilo , inaczej list zostanie zignorowany

Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).

Liczba znaków do wykorzystania: 2048

 

W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień szeroko opisywanych w podręcznikach.



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

©2017 mgr Jerzy Wałaszek

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