Powierzchnie graficzne w SDL

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

 

Artykuł w PRZEBUDOWIE


SDL

Biblioteka SDL udostępnia powierzchnie rysunkowe, które odwzorowują obszary ekranu graficznego komputera. Dostęp do bufora ekranowego uzyskujemy poprzez strukturę SDL_Surface. Zanim opiszemy jej pola oraz sposób tworzenia, omówimy podstawowe formaty pikseli (ang. pixel = picture element), z którymi możemy się spotkać we współczesnych komputerach. Oczywiście pominiemy archaiczne rozwiązania w stylu obraz monochromatyczny czy 16 kolorów VGA. Współczesne karty graficzne pozwalają na pracę w pełnym kolorze 32-bitowym.

Grafika:Liquid Crystal Display Macro Example zoom.jpg

Obraz na monitorze ekranowym zbudowany jest z drobnej siatki punktów, zwanych pikselami. Siatka obrazowa nazywana jest często rastrem - stąd pochodzi również termin grafika rastrowa (ang. raster graphics). Jeśli spojrzysz na ekran swojego monitora przy pomocy lupy o dużym powiększeniu, to zauważysz, iż każdy piksel zawiera triadę (tzn. trzy) punktów jednobarwnych - czerwonego, zielonego oraz niebieskiego (ang. RGB - red, green, blue). Natężenie świecenia (jasność) tych punktów daje w efekcie różne barwy pośrednie.

Na przykład barwa żółta powstanie, gdy świecą punkty czerwony i zielony. Barwę białą otrzymujemy, gdy świecą wszystkie trzy punkty z odpowiednią jasnością. To "oszustwo" działa, ponieważ ludzkie oko nie rozróżnia tak małych obszarów i sumuje odbierane barwy - w efekcie widzimy barwę wynikową, a nie jej poszczególne składowe. Po prawej stronie masz przykład mieszania barw podstawowych (sprawdź za pomocą lupy świecące obszary!).

Obszar pamięci przechowujący wyświetlany obraz (lub treść okna graficznego) będziemy nazywali buforem obrazowym (ang. frame buffer) lub powierzchnią graficzną (ang. graphic surface). Każdy piksel obrazu jest odwzorowany w buforze przez grupę sąsiednich komórek pamięci - w zależności od trybu pracy karty graficznej jest to od 1 do 4 bajtów. Bufor zajmuje ciągły obszar pamięci. Pierwszą pozycją w buforze obrazowym jest piksel znajdujący się na obrazie w lewym górnym rogu. Następną pozycją jest kolejny piksel na prawo itd. Poniżej przedstawiamy sposób odwzorowania pikseli w buforze - obraz ma wymiar 4 x 4 piksele, wewnątrz pikseli umieściliśmy dla orientacji ich współrzędne x,y na obrazie:

            Współrzędna x:          
              0 1 2 3            
            0 00 10 20 30 Linia 0
Współrzędna y: 1 01 11 21 31 Linia 1
2 02 12 22 32 Linia 2
            3 03 13 23 33 Linia 3
  Linia 0 Linia 1 Linia 2 Linia 3
Bufor obrazowy: 00 10 20 30 01 11 21 31 02 12 22 23 03 13 23 33
Pozycje w buforze: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Jeśli znamy współrzędne x,y piksela oraz szerokość ekranu (liczbę pikseli w linii - w przykładzie jest to 4), to pozycja w buforze może być wyliczona wg wzoru:

pozycja = y • szerokość linii + x

Na przykład dla x = 2, y = 3 mamy:

pozycja w buforze = 3 • 4 + 2 = 14

Znając adres bufora ekranowego, numer pozycji w buforze oraz ilość bajtów na piksel możemy w prosty sposób obliczyć adres piksela w pamięci:

adres piksela = adres bufora + pozycja w buforze * ilość bajtów na piksel

Istnieją dwa podstawowe sposoby odwzorowania pikseli w pamięci:

- tryb paletowy (ang. palette video mode)

- tryb RGB (ang. RGB video mode)

Tryb paletowy

Piksel reprezentowany jest przez 1 bajt w pamięci bufora obrazu. W kodzie piksela zawarty jest numer pozycji w palecie barw, która definiuje kolor. Jest to zatem odwzorowanie pośrednie: tworzymy tablicę barw, której każdy element definiuje jeden kolor na ekranie. Taka tablica zwana jest paletą. W palecie można umieścić do 256 definicji kolorów. Piksele na obrazie zawierają numery kolorów palety. Ideę działania tego trybu wideo przedstawia poniższy rysunek - w pikselach umieściliśmy ich kody:

Piksele: 5 2 2 2 2 2 2 6
5 2 2 2 2 2 2 6
6 6 6 4 3 6 6 6
1 1 1 3 3 1 0 0
                 
Paleta:                                
Numer: 0 1 2 3 4 5 6 7

Zaletą trybu paletowego jest oszczędność pamięci. Można również wykorzystywać go do animacji - zmiana definicji koloru w palecie powoduje automatyczną zmianę koloru wszystkich pikseli na obrazie, które zawierają numer tego koloru palety. Zatem przez pojedynczy wpis w palecie barw możemy szybko zmieniać kolory całych obszarów.

Obecnie tryby paletowe są wykorzystywane głównie w plikach graficznych - np. słynny format GIF.

Tryb RGB

Tryby RGB noszą nazwę trybów pełno kolorowych (ang. true color mode).  Informacja o kolorze zapisana jest bezpośrednio wewnątrz kodu piksela. Każda z barw podstawowych: R (red) - czerwona, G (green) - zielona i B (blue) - niebieska posiada w kodzie piksela pole bitowe zawierające jej wartość. Ilość bitów pola określa liczbę poziomów jasności związanej z nim barwy podstawowej. Możliwe są następujące formaty (komputery IBM-PC):

16 bitów na piksel:
A R R R R R G G G G G B B B B B

5/5/5 - każda barwa podstawowa ma pole 5 bitowe. Liczba wszystkich kolorów wynosi 25 • 25 • 25 = 25+5+5 = 215 = 32768. Jeden bit w kodzie piksela nie jest wykorzystywany do reprezentacji barwy. Może być używane np. do definiowania przezroczystości - tzw. kanał alfa.

 

R R R R R G G G G G G B B B B B

5/6/5 - pola koloru czerwonego i niebieskiego mają po 5 bitów szerokości, natomiast pole koloru zielonego jest sześciobitowe. Ten wybór podyktowany był własnościami ludzkiego oka, które znacznie lepiej rozróżnia barwy zawierające składową zieloną - to pozostałość z życia w dżungli. Liczba możliwych kolorów wyraża się wzorem 25 • 26 • 25 = 25+6+5 = 216 = 65536.

Z trybów 16-bitowych obecnie korzysta się coraz rzadziej. Również my nie będziemy się nimi zajmować.

24 bity na piksel
R R R R R R R R G G G G G G G G B B B B B B B B

8/8/8 - wszystkie barwy podstawowe posiadają w kodzie piksela pola 8 bitowe. Całkowita liczba możliwych do uzyskania kolorów jest równa 28 • 28 • 28 = 28+8+8 = 224 = 16777216. Taka ilość barw przekracza możliwości percepcji oka człowieka. Tryb 24 bitowy występuje dzisiaj już rzadko w kartach graficznych - spotkać go można jeszcze jedynie w starszych modelach. Spowodowane to jest spadkiem efektywności dostępu do pamięci przez współczesne procesory 32 i 64 bitowe - piksele nie leżą bowiem na adresach podzielnych przez 4.

32 bity na piksel
A A A A A A A A R R R R R R R R G G G G G G G G B B B B B B B B

8/8/8 - tryb równoważny trybowi 24-bitowemu. Taki format wybrano ze względu na szybki, 32-bitowy dostęp do pamięci przez współczesne procesory Pentium - jeśli dane 32 bitowe leżą w całości pod adresem podzielnym przez 4, to procesor Pentium potrzebuje tylko jednego cyklu dostępu do pamięci. Najstarszy bajt jest albo nie używany, albo określa stopień przezroczystości piksela - kanał alfa.


UWAGA:

Nie musisz od razu starać się zrozumieć wszystkich podanych poniżej informacji. Większość z nich nie będzie nam potrzebna lub też będziemy z nich korzystać sporadycznie. Zatem jeśli coś jest dla ciebie niejasne, nie przejmuj się. Opisy struktur podajemy z zapasem na przyszłość. Stronę możesz wydrukować i traktować jak instrukcję do struktur graficznych biblioteki SDL. Jeśli znasz język angielski, to podobny opis znajdziesz w plikach pomocy, dostarczanych w dystrybucji SDL. Jednakże tutaj masz zebrane wszystkie informacje razem.

Struktura SDL_Surface

W bibliotece SDL bufor obrazowy opisuje struktura SDL_Surface o następujących polach danych:

typedef struct
{
  Uint32 flags;
  SDL_PixelFormat * format;
  int w, h;
  Uint16 pitch;
  void * pixels;
  SDL_Rect clip_rect;
  int refcount;
} SDL_Surface;

Typy Uint8, Uint16 i Uint32 oznaczają odpowiednio 8, 16 i 32 bitowe liczby bez znaku. Zakresy wartości są następujące:

Uint8   0...28 - 1   0...255
Uint16     0...216 - 1     0...65535
Uint32   0...232 - 1   0...4294967295  

Typy Sint8, Sint16 i Sint32 oznaczają odpowiednio 8, 16 i 32 bitowe liczby ze znaku. Zakresy wartości są następujące:

Sint8   -27 - 27 - 1   -128...127
Sint16     -215 - 215 - 1     -32768...32767
Sint32   -231 - 231 - 1   -2147483648...2147483647  
flags - określa parametry powierzchni graficznej. Istotne są ustawione bity tego pola, które posiadają zdefiniowane stałe:
SDL_SWSURFACE  -  powierzchnia jest umieszczona w pamięci systemowej (SW = SoftWare)
SDL_HWSURFACE  -  powierzchnia jest umieszczona w pamięci karty graficznej (HW = HardWare)
SDL_ASYNCBLIT  -  jeśli jest to możliwe, powierzchnia wykorzystuje asynchroniczne przesyłanie (BLITS) danych na ekran
SDL_ANYFORMAT  -  zezwala na dowolny format pikseli
SDL_HWPALETTE  -  powierzchni posiada zarezerwowaną tylko dla siebie paletę kolorów
SDL_DOUBLEBUF  -  powierzchnia jest podwójnie buforowana.
SDL_FULLSCREEN  -  powierzchnia wypełnia cały ekran.
SDL_OPENGL  -  powierzchnia pracuje w kontekście OpenGL
SDL_OPENGLBLIT  -  powierzchnia umożliwia uaktualnianie obrazu za pomocą przesłań OpenGL.
SDL_RESIZABLE  -  powierzchnia może zmieniać rozmiar
SDL_HWACCEL  -  uaktualnianie obrazu wykorzystuje akcelerator graficzny karty
SDL_SRCCOLORKEY  -  powierzchnia wykorzystuje uaktualnianie wg klucza koloru
SDL_RLEACCEL  -  uaktualnianie wg klucza koloru jest przyspieszane za pomocą RLE
SDL_SRCALPHA  -  uaktualnianie wykorzystuje przeźroczystość kanału alfa
SDL_PREALLOC  -  powierzchnia wykorzystuje wcześniej zarezerwowany obszar pamięci
format - zawiera adres struktury typu SDL_PixelFormat, która definiuje sposób odwzorowania pikseli w pamięci bufora obrazowego. Struktura posiada następujące pola danych:
typedef struct
{
  SDL_Palette *palette;
  Uint8  BitsPerPixel;
  Uint8  BytesPerPixel;
  Uint32 Rmask, Gmask, Bmask, Amask;
  Uint8  Rshift, Gshift, Bshift, Ashift;
  Uint8  Rloss, Gloss, Bloss, Aloss;
  Uint32 colorkey;
  Uint8  alpha;
} SDL_PixelFormat;
palette  -   jeśli powierzchnia pracuje w trybie paletowym, pole zawiera adres struktury SDL_Palette definiującej paletę. W przeciwnym razie spodziewaj się tutaj adresu NULL. Struktura typu SDL_Palette posiada następujące pola:
typedef struct
{
  int ncolors;
  SDL_Color *colors;
} SDL_Palette;
ncolors   liczba kolorów używana w palecie
colors  - wskaźnik do tablicy typu SDL_Color definiującej poszczególne kolory palety. Każdy element tablicy składa się z czterech pól, z których trzy pierwsze określają natężenie trzech kolorów składowych - r czerwony, g - zielony i b niebieski. Ostatnie pole jest nieużywane i służy jedynie do wyrównania elementów tablicy do adresów podzielnych przez 4 (procesory pentium szybciej pracują z danymi 32 bitowymi umieszczonymi w pamięci w ten właśnie sposób).
typedef struct
{
  Uint8 r;
  Uint8 g;
  Uint8 b;
  Uint8 unused;
} SDL_Color;
BitsPerPixel  - określa liczbę bitów reprezentujących piksel w buforze obrazowym. Pole to zawiera zwykle wartości 8 (tryb paletowy), 16, 24 lub 32 (tryby RGB).
BytesPerPixel  - zawiera liczbę bajtów (komórek pamięci) zajmowanych przez piksel w buforze obrazowym. Zwykle jest to od 1 (tryb paletowy) do 4 (tryby RGB).
Rmask
Gmask
Bmask
Amask
 - w trybach RGB bity reprezentujące piksel w pamięci bufora obrazowego są podzielone na cztery grupy - tzw. pola bitowe. Pola (RGBA)mask pozwalają wydobyć operacją & z kodu binarnego piksela obszary przechowujące wartości poszczególnych barw RGB oraz kanału przezroczystości A (ang. Alpha channel). Na przykład załóżmy, iż piksel zawiera 16 bitów podzielonych na cztery pola R - 5 bitów, G - 5 bitów, B - 5 bitów i A - 1 bit. Wtedy:
piksel 0000000000000000 A R R R R R G G G G G B B B B B
Rmask   0000000000000000 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
Gmask 0000000000000000 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0
Bmask 0000000000000000 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1
Amask 0000000000000000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Rshift
Gshift
Bshift
Ashift
 - określa przesunięcie bitowe pól zawierających wartości składowych RGB i kanału Alfa w kodzie piksela. Dla przykładu podanego powyżej pola te zawierają następujące wartości:
Rshift = 10
Gshift = 5
Bshift = 0
Ashift = 15
Rloss
Gloss
Bloss
Aloss
 - wartości składowych RGB są 8 bitowe. Jeśli w kodzie piksela zarezerwowane jest mniej bitów na składową (w naszym przykładzie jest to 5 bitów), to najmłodsze bity zostaną utracone - nie ma ich gdzie przechować. Pola (RGBA)loss określają ile najmłodszych bitów jest odrzucane z każdej składowej przy umieszczeniu jej w kodzie piksela. W naszym przykładzie otrzymamy następujące wartości:
Rloss = 3
Gloss = 3
Bloss = 3
Aloss = 7
colorkey  - kod pikseli przezroczystych.
alpha  - wartość przezroczystości dla całej powierzchni.
w,h - szerokość (ang. width) oraz wysokość (ang. height) powierzchni graficznej w pikselach.
pitch - ilość bajtów na jedną linię ekranową - pamięć bufora odwzorowuje tworzenie obrazu na ekranie monitora. Obraz składa się z kolejno wyświetlanych od góry w dół linii. Każda linia składa się z jednego wiersza pikseli. W buforze obrazu linia odwzorowana jest przez podaną w polu pitch liczbę bajtów. Wartość ta pozwala nam obliczyć adres każdego piksela w buforze na podstawie jego współrzędnych ekranowych.
pixels - wskaźnik obszaru pamięci przechowującej bufor obrazu.
clip_rect - definiuje prostokąt na powierzchni graficznej, którego wnętrze będzie przesyłane na ekran - pozwala to ograniczyć np. uaktualnianie powierzchni graficznej tylko do obszaru faktycznych zmian. Jeśli pole clip_rect ma wartość NULL, to w operacjach uczestniczy cała powierzchnia graficzna.  W przeciwnym razie pole wskazuje strukturę SDL_Rect:
typedef struct
{
  Sint16 x, y;
  Uint16 w, h;
} SDL_Rect;

x, y - pozycja lewego górnego narożnika prostokąta
w, h - szerokość i wysokość prostokąta

refcount - licznik odwołań do struktury SDL_Surface. Wykorzystywany jest przy zwalnianiu pamięci.

Podsumowanie pól struktury SDL_Surface

Tworzenie bufora obrazu

Pierwszą operacją programu jest utworzenie bufora obrazu, po którym będziemy rysowali. Do tego celu w bibliotece SDL mamy funkcję SDL_SetVideoMode o następującej definicji:

SDL_Surface *SDL_SetVideoMode(int width, int height, int bpp, Uint32 flags);

Funkcja zwraca wskaźnik do tworzonej struktury SDL_Surface. Jeśli zwrócony zostanie adres NULL, to struktury SDL_Surface nie dało się utworzyć z powodu jakiegoś błędu. Nie możemy wtedy kontynuować wykonywania programu, gdyż program nie będzie posiadał bufora obrazowego. Zwrócony adres należy przechować w odpowiedniej zmiennej. Robimy to wg poniższego schematu:

...

SDL_Surface * screen; // tworzymy zmienną na wskaźnik do struktury SDL_Surface

...

  if(!(screen = SDL_SetVideoMode(parametry obrazu)))
  {
    // tutaj umieszczamy instrukcje do wykonania, gdy nie można utworzyć bufora
    ...
  }

...
width, height   -   określają wymiary powierzchni w pikselach - kolejno szerokość i wysokość.
bpp  - pożądana ilość bitów na piksel, tryb paletowy 8, pozostałe tryby 16, 24 lub 32 (u nas będziemy wykorzystywać 32).
flags  - znaczniki tworzonej powierzchni. Można stosować stałe opisane dla pola flags struktury SDL_Surface (u nas będziemy wykorzystywać znaczniki SDL_SWSURFACE, SDL_HWSURFACE, SDL_DOUBLEBUF i SDL_FULLSCREEN).

Poniższy przykład tworzy powierzchnię graficzną w pamięci karty video o rozmiarach 320 x 240 pikseli. Piksele mają głębię 32-bitową.

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne
//
// P001 - tworzenie powierzchni graficznej
//----------------------------------------

#include <windows.h>
#include <SDL/SDL.h>

int main(int argc, char *argv[])
{
  SDL_Surface * screen;
  
// inicjujemy bibliotekę SLD

  if(SDL_Init(SDL_INIT_VIDEO))
  {
    MessageBox(0, SDL_GetError(), "Błąd inicjalizacji SDL", MB_OK);
    exit(-1);         // wyświetlamy opis błędu i kończymy
  }

  atexit(SDL_Quit); // przy zakończeniu będzie wywołane SDL_Quit
  
// tworzymy bufor obrazowy 340 x 280 pikseli o głębi 32 bitów

  if(!(screen = SDL_SetVideoMode(320, 240, 32, SDL_HWSURFACE)))
  {
    MessageBox(0, SDL_GetError(), "Błąd tworzenia bufora obrazowego", MB_OK);
    exit(-1);         // wyświetlamy opis błędu i kończymy
  }

// wszystko w porządku. Obszar graficzny pojawia się na ekranie monitora.

  MessageBox(0, "Wszystko OK", "Ekran SDL", MB_OK);
}

Sprawdź co się stanie, gdy w programie zmienisz wywołanie:
    screen = SDL_SetVideoMode(320, 240, 32, SDL_HWSURFACE)
na
    screen = SDL_SetVideoMode(320, 240, 40, SDL_HWSURFACE)

Blokowanie powierzchni graficznej

W środowisku wielowątkowym powierzchnia graficzna zwykle musi zostać zablokowana dla innych wątków przed rozpoczęciem rysowania. W ten sposób unikamy wzajemnych zakłóceń. Ponieważ jednak nie każda powierzchnia wymaga blokowania, stosujemy następującą konstrukcję:

...

// blokujemy ekran przed dostępem do jej treści

  if(SDL_MUSTLOCK(screen))
    if(SDL_LockSurface(screen) < 0)
    {
      MessageBox(0, SDL_GetError(), "Błąd blokady bufora obrazowego", MB_OK);
      exit(-1);         // wyświetlamy opis błędu i kończymy
    }


// tutaj rysujemy po powierzchni

    ...

// odblokowujemy bufor

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

...

SDL_MUSTLOCK(SDL_Surface * ekran) - zwraca wartość różną od zera, jeśli powierzchnia musi być blokowana.
SDL_LockSurface(SDL_Surface * ekran) - blokuje powierzchnię w celu bezpośredniego dostępu do pikseli
SDL_UnlockSurface(SDL_Surface * ekran) - odblokowuje powierzchnię.

Pomiędzy parą wywołań funkcji SDL_LockSurface() i SDL_UnlockockSurface() nie powinieneś korzystać z żadnych funkcji systemowych lub bibliotecznych, gdyż może to spowodować nieprzewidywalne błędy pamięciowe.

Uaktualnianie treści ekranu

Gdy zmienisz zawartość bufora (już za chwilę to się stanie), musisz odświeżyć treść ekranu. Operacji tej dokonuje funkcja:

void SDL_UpdateRect(SDL_Surface * screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h);

screen - wskazuje strukturę SDL_Surface
x,  y - współrzędne lewego górnego narożnika prostokąta, którego zawartość ma być uaktualniona
w, h - szerokość i wysokość prostokąta do uaktualnienia

Jeśli parametry x, y, w i h przyjmą wartość 0, to uaktualniany jest cały obszar graficzny. Nie jest to specjalnie ekonomiczne czasowo, jeśli zmiany dokonaliśmy tylko w małym fragmencie ekranu.

Uwaga: funkcji SDL_UpadteRect() nie wolno wywoływać w trakcie zablokowania bufora.

Rysowanie pikseli na powierzchni graficznej

I na koniec mamy już wszystkie potrzebne wiadomości, aby rozpocząć rysowanie po obszarze graficznym. W przypadku ogólnym procedura rysująca piksele powinna rozpoznawać ich format. Wszystko możemy odczytać ze struktury SDL_Surface z pola format, wskazującego strukturę SDL_PixelFormat. Jednakże u nas ekran będzie zawsze wykorzystywał głębię 32-bitową koloru, czyli pojedynczy piksel zajmuje w buforze obrazowym 4 kolejne bajty. Rysowanie wykonujemy wg następującego algorytmu:

  1. Blokujemy powierzchnię graficzną.
  2. Obliczamy kolor piksela lub mamy go zadany z góry. W przypadku głębi 32 bitowej poszczególne składowe zajmują trzy pierwsze bajty w pikselu. Jeśli kod piksela potraktujemy szesnastkowo, to kolory składowe układają się wg schematu: 0xrrggbb. Na przykład kod 0xff0000 to kolor czerwony, kod 0xffff00 to kolor żółty, a 0xff00ff to fioletowy.
  3. Na podstawie współrzędnych x i y obliczamy adres piksela w buforze. Jeśli screen jest wskaźnikiem struktury SDL_Surface, to adres obliczamy następująco:

      p = (Uint32 *) screen->pixels + y • screen->w + x
     
  4. Pod wyznaczonym adresem umieszczamy kod piksela:

      * p = kod_piksela
     
  5. Operacje 2,3 i 4 powtarzamy pożądaną ilość razy
  6. Odblokowujemy powierzchnię graficzną
  7. Uaktualniamy obszar, na którym postawiliśmy piksele.

Poniższy program tworzy bufor obrazowy i umieszcza na jego środku piksel w kolorze żółtym.

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne
//
// P002 - rysowanie piksela w buforze ekranu
//------------------------------------------

#include <windows.h>
#include <SDL/SDL.h>

int main(int argc, char *argv[])
{
  SDL_Surface * screen;
  
// inicjujemy bibliotekę SLD

  if(SDL_Init(SDL_INIT_VIDEO))
  {
    MessageBox(0, SDL_GetError(), "Błąd inicjalizacji SDL", MB_OK);
    exit(-1);         // wyświetlamy opis błędu i kończymy
  }

  atexit(SDL_Quit);   // przy zakończeniu będzie wywołane SDL_Quit
  
// tworzymy bufor obrazowy 320 x 240 pikseli

  if(!(screen = SDL_SetVideoMode(320, 240, 32, SDL_HWSURFACE)))
  {
    MessageBox(0, SDL_GetError(), "Błąd tworzenia bufora obrazowego", MB_OK);
    exit(-1);         // wyświetlamy opis błędu i kończymy
  }

// blokujemy ekran przed

  if(SDL_MUSTLOCK(screen))
    if(SDL_LockSurface(screen) < 0)
    {
      MessageBox(0, SDL_GetError(), "Błąd blokady bufora obrazowego", MB_OK);
      exit(-1);        // wyświetlamy opis błędu i kończymy
    }

// tutaj rysujemy po powierzchni

  int x, y, color;
  Uint32 * p;
  
  color = 0xffff00;  // kolor jasnożółty
  
  x = screen->w >> 1; // wyznaczamy środek ekranu
  y = screen->h >> 1;
  
  p = (Uint32 *)screen->pixels + y * screen->w + x;
  
  * p = color; // rysujemy piksel

// odblokowujemy bufor

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

// uaktualniamy obszar z narysowanym pikselem

  SDL_UpdateRect(screen, x, y, 1, 1);
  
// Koniec programu

  MessageBox(0, "To wszystko chłopy.", "Aplikacja SDL", MB_OK);
}

Efekt może nie jest jeszcze specjalnie imponujący, ale na następnych zajęciach zajmiemy się tworzeniem prostej biblioteki procedur graficznych, które będziemy wykorzystywać w przyszłych programach do nauki tworzenia grafiki komputerowej. Podstawy już mamy, zatem nie przerażaj się ilością wiedzy. W sumie nie jest ona aż tak skomplikowana, a programowanie aplikacji SDL to prawdziwa przyjemność w porównaniu do walki z "czystym" systemem MS-Windows!

Dla przykładu poniższy program wypełnia obszar obrazowy dosyć ciekawym gradientem koloru. Program po prostu rysuje poszczególne piksele, których kolor zależy od ich aktualnego położenia w obszarze graficznym. Poeksperymentuj z nim, być może uda ci się uzyskać ciekawsze efekty graficzne.

// I Liceum Ogólnokształcące
// w Tarnowie
// Koło informatyczne
//
// P003 - wypełnianie bufora obrazowego
//-------------------------------------

#include <windows.h>
#include <SDL/SDL.h>

const int SW = 320; // szerokość ekranu
const int SH = 240; // wysokość ekranu

int main(int argc, char *argv[])
{
  SDL_Surface * screen;
  
// inicjujemy bibliotekę SLD

  if(SDL_Init(SDL_INIT_VIDEO))
  {
    MessageBox(0, SDL_GetError(), "Błąd inicjalizacji SDL", MB_OK);
    exit(-1);         // wyświetlamy opis błędu i kończymy
  }

  atexit(SDL_Quit);   // przy zakończeniu będzie wywołane SDL_Quit
  
// tworzymy bufor obrazowy SE x WE pikseli

  if(!(screen = SDL_SetVideoMode(SW, SH, 32, SDL_HWSURFACE)))
  {
    MessageBox(0, SDL_GetError(), "Błąd tworzenia bufora obrazowego", MB_OK);
    exit(-1);         // wyświetlamy opis błędu i kończymy
  }

    
// blokujemy ekran

  if(SDL_MUSTLOCK(screen))
    if(SDL_LockSurface(screen) < 0)
    {
      MessageBox(0, SDL_GetError(), "Błąd blokady bufora obrazowego", MB_OK);
      exit(-1);        // wyświetlamy opis błędu i kończymy
    }
  
  int x, y, color, dr, db, dg;
  Uint32 * p;
  
  for(x = 0; x < SW; x++)
    for(y = 0; y < SH; y++)
    {

// obliczamy kolor piksela - poszczególne składowe RGB zależą od współrzędnych
// piksela w obszarze powierzchni graficznej.

      dr = (x * 255) / (SW - 1);
      dg = (y * 255) / (SH - 1);
      db = ((SW + SH - x - y - 1) * 255) / (SW + SH - 1);

// łączymy składowe RGB w kod piksela

      color = (dr << 16) | (dg << 8) | db;
        
      p = (Uint32 *)screen->pixels + y * screen->w + x;
  
      * p = color; // rysujemy piksel
    }

// odblokowujemy bufor

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

// uaktualniamy obszar całego ekranu

  SDL_UpdateRect(screen, 0, 0, 0, 0);
  
// Koniec programu

  MessageBox(0, "Koniec.", "Aplikacja SDL", MB_OK);
}


Podsumowanie

SDL_Surface - struktura przechowująca informację o buforze obrazowym

SDL_PixelFormat - struktura opisująca sposób kodowania koloru w pikselach. Dostęp poprzez pole format struktury SDL_Surface.

SDL_Palette - struktura opisująca paletę kolorów w trybach paletowych

SDL_Color - struktura opisująca definicję koloru w palecie

SDL_Rect - struktura wykorzystywana do definiowania prostokątnego obszaru w obrębie powierzchni graficznej

bufor obrazowy - obszar pamięci, w którym odwzorowana jest treść obrazu graficznego.

 

SDL_SetVideoMode(szerokość,wysokość,głębia,parametry ekranu) - tworzy w pamięci strukturę SDL_Surface i zwraca jej adres.

SDL_MUSTLOCK(wskaźnik SDL_Surface) zwraca wartość różną od zera, jeśli bufor obrazowy musi być blokowany przy bezpośrednim dostępie do jego pamięci.

SDL_LockSurface(wskaźnik SDL_Surface) - blokuje bufor obrazowy danej struktury SDL_Surface

SDL_UnlockSurface(wskaźnik SDL_Surface) - zwalnia blokadę bufora obrazowego

SDL_UpdateRect(wskaźnik SDL_Surface,x,y,szerokość,wysokość) - uaktualnia treść obrazu z bufora zawartą we wskazanym prostokącie.



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.