Serwis Edukacyjny
Nauczycieli
w I-LO w Tarnowie

obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

Autor artykułu: mgr Jerzy Wałaszek
Uaktualniono: 31.07.2022

©2024 mgr Jerzy Wałaszek
I LO w Tarnowie

Kontekst graficzny

SPIS TREŚCI
Podrozdziały

Akcelerator graficzny

W rozdziale o grafice rastrowej opisałem krótko historię kart graficznych:

HGC = Hercules Graphic Card

CGA = Color Graphic Adapter

EGA = Enhanced Graphic Adapter

VGA = Video Graphic Array

SVGA = Super Video Graphic Array

Współczesna karta graficzna nVidia

Przypomnijmy:

Karta graficzna (ang. graphic card) jest urządzeniem, które tworzy obraz wyświetlany na monitorze komputera. Karta jest wsuwana w odpowiedni slot na płycie głównej komputera. Począwszy od mikroprocesorów 64-bitowych firma Intel integruje karty graficzne z procesorem głównym. Zintegrowana karta nie ma specjalnie dużych osiągnięć, niemniej zwykle wystarcza do typowych zastosowań: grafika biurowa, wyświetlanie filmów, prostsze gry komputerowe, itp. Wymagający użytkownicy muszą się zaopatrzyć w dedykowane karty graficzne o wysokich parametrach. Ale nie o tym będziemy tutaj rozprawiać.

Zwykle karta posiada własną pamięć na grafikę, VRAM (ang. Video-RAM). W pamięci tej przechowywana jest treść wyświetlanego obrazu w postaci tzw. bufora ekranowego (ang. frame buffer). Obraz zbudowany jest z siatki pikseli zwanej rastrem. Raster posiada określoną liczbę linii oraz liczbę pikseli w linii, np. 1024 piksele na 768 linii. W buforze ekranu przechowywana jest informacja o kolorach pikseli w poszczególnych liniach obrazu (od strony lewej do prawej). Taka organizacja odpowiada kolejności przesyłania pikseli do monitora. Układ graficzny karty cyklicznie (np. 60 razy na sekundę) odczytuje zawartość bufora ekranowego bajt po bajcie, zamienia tą informację na informację o kolorze pikseli i przesyła szeregowo do monitora poprzez złącze graficzne.

Zwykle pamięć VRAM jest większa niż wielkość bufora ekranowego. Nadmiar pamięci karta może wykorzystywać do przechowywania dodatkowych danych graficznych, tzw. tekstur. Tekstura (ang. texture) to obrazek, który znajduje się w pamięci karty graficznej. Czasem chcemy przetwarzać obrazki za pomocą programu, wtedy umieszczamy je w pamięci RAM procesora. Obrazek w pamięci RAM będziemy nazywali bitmapą (ang. bitmap) lub powierzchnią (ang. surface).

W dzisiejszych kartach graficznych zwykle nie wykonuje się operacji bezpośrednio na buforze ekranu, lecz na osobnym obszarze. Gdy operacja się zakończy, zawartość obszaru zostaje przesłana do bufora ekranowego w chwili,, gdy obraz nie jest generowany (np. w przerwie pomiędzy dwoma ramkami). W ten sposób wyświetlany obraz nie jest zakłócany zmianami częściowymi. W trakcie tego kursu tak właśnie będziemy robić.

Akcelerator graficzny (ang. graphic accelerator) jest specjalizowanym układem, który sprzętowo (zatem bardzo szybko) wykonuje różne operacje graficzne na teksturach i buforze ekranowym. Zaletą jest to, iż operacje te są wykonywane setki razy szybciej od procesora głównego i jednocześnie ich wykonanie odciąża procesor, który w tym czasie może zająć się czymś innym. Współczesne akceleratory są bardzo skomplikowane i potrafią wykonywać operacje trójwymiarowe. SDL2 korzysta z usług akceleratora.


Na początek:  podrozdziału   strony 

Struktury graficzne w SDL

Zanim zabierzemy się poważnie za programowanie grafiki, musisz poznać i zrozumieć podstawowe struktury danych używane w tej bibliotece.

Pierwszą taką strukturę już poznałeś w poprzednim rozdziale:

SDL_Window struktura zawierająca informacje dotyczące okna utworzonego przy pomocy funkcji SDL_CreateWindow(). Definicja tej struktury jest ukryta przed programistą, ponieważ jej zawartość wykorzystują różne funkcje biblioteczne i użytkownik nie musi tutaj ingerować. Nawet nie zaleca się odwoływania do elementów tej struktury, ponieważ w przyszłych wydaniach biblioteki SDL zawartość może ulec zmianie. Niemniej jednak poniżej przedstawiam definicję:
struct SDL_Window
{
    const void *magic;
    Uint32 id;
    char *title;
    SDL_Surface *icon;
    int x, y;
    int w, h;
    int min_w, min_h;
    int max_w, max_h;
    Uint32 flags;
    Uint32 last_fullscreen_flags;
    SDL_Rect windowed;
    SDL_DisplayMode fullscreen_mode;
    float brightness;
    Uint16 *gamma;
    Uint16 *saved_gamma;
    SDL_Surface *surface;
    SDL_bool surface_valid;
    SDL_bool is_hiding;
    SDL_bool is_destroying;
    SDL_WindowShaper *shaper;
    SDL_HitTest hit_test;
    void *hit_test_data;
    SDL_WindowUserData *data;
    void *driverdata;
    SDL_Window *prev;
    SDL_Window *next;
};

Często będziemy musieli określać obszary prostokątne. Do tego celu w SDL2 mamy odpowiednią strukturę:

SDL_Rect struktura definiuje prostokątny obszar na powierzchni graficznej:
struct SDL_Rect
{
  Sint16 x, y;
  Uint16 w, h;
};

x,y – współrzędne ekranowe lewego górnego narożnika. Zwróć uwagę, że mogą być ujemne.
w – szerokość prostokąta w pikselach
h – wysokość prostokąta w pikselach

Kolory pikseli w SDL również definiowane są odpowiednią strukturą:

SDL_Color struktura definiuje kolor za pomocą składowych:
struct SDL_Color
{
  Uint8 r;
  Uint8 g;
  Uint8 b;
  Uint8 unused;
};

r – składowa czerwona (ang. red) 0...255
g – składowa zielona (ang. green) 0...255
b – składowa niebieska (ang. blue) 0...255

Do definiowania punktów służy struktura:

SDL_Point struktura określa położenie punktu na obrazie:
struct SDL_Point
{
  int x;
  int y;
};

x – współrzędna pozioma punktu
y – współrzędna pionowa punktu
Uwaga: oś y jest skierowana w dół, zatem punkty o większej współrzędnej y leżą niżej na obrazie.

Karty graficzne mogą posiadać różne sposoby kodowania kolorów pikseli. W prostszych urządzeniach stosowane są tryby paletowe (biblioteka SDL2 może być używana nie tylko na komputerach IBM PC). W rozdziale o grafice rastrowej opisaliśmy zasadę działania trybów paletowych. Tryby te były również stosowane w starszych kartach graficznych. Krótko mówiąc w trybie paletowym piksel w buforze ekranu nie zawiera bezpośrednio informacji o kolorze, lecz numer koloru palety, Takie rozwiązanie pozwala zaoszczędzić pamięci. Na przykład, jeśli każdy piksel jest reprezentowany przez 4 bity, to może przyjmować wartości od 0 do 15, a zatem na ekranie jednocześnie może się pojawić 16 różnych kolorów. Dla każdej wartości piksela w palecie zdefiniowany jest odpowiedni kolor, który zostanie wyświetlony dla danego piksela. Paleta może być zdefiniowana sprzętowo (kolory są ustalone na stałe) lub definiowana przez program użytkownika. Do przechowywania używanej palety kolorów SDL posiada specjalną strukturę:

SDL_Palette struktura definiuje kolory dla pikseli 8-bitowych:
struct SDL_Palette
{
  int ncolors;
  SDL_Color *colors;
};

ncolors – liczba kolorów w palecie
colors – wskaźnik tablicy z elementami typu SD_Color, które definiują poszczególne kolory

W trybach bezpośrednich informacja o kolorze zawarta jest w kodzie piksela. Np. dla trybu 24 bitowego RGB dla każdej składowej koloru przydzielone jest 8 bitów piksela. Piksel zajmuje w buforze 3 bajty, lecz może być w ponad 16 milionach kolorów.

Informacja o sposobie kodowania kolorów w pikselach przechowywana jest w specjalnej strukturze:

SDL_PixelFormat struktura definiuje strukturę pikseli
struct SDL_PixelFormat
{
  SDL_Palette *palette;
  Uint8  BitsPerPixel;
  Uint8  BytesPerPixel;
  Uint8  Rloss, Gloss, Bloss, Aloss;
  Uint8  Rshift, Gshift, Bshift, Ashift;
  Uint32 Rmask, Gmask, Bmask, Amask;
  Uint32 colorkey;
  Uint8  alpha;
};

palette – wskaźnik palety, jeśli rozmiar piksela nie przekracza 8 bitów. Dla pikseli o rozmiarze ponad 8 bitów, pole zawiera NULL, czyli wskaźnik zerowy.
BitsPerPixel – liczba bitów na piksel. Zwykle 8,16, 24 lub 32.
BytesPerPixel – liczba bajtów na piksel. Zwykle od 1 do 4.
R, G, B – składowe kolorów czerwona, zielona i niebieska
A – przezroczystość
xlos – określa liczbę bitów składowej 8-bitowej (R, G, B lub A) traconych przy umieszczeniu jej w kodzie piksela. Na przykład, jeśli pole składowej w pikselu ma rozmiar 5 bitów, to strata wyniesie 3 bity.
xshift – przesunięcie w bitach (w lewo) składowej w kodzie piksela. Na przykład, jeśli kodowanie kolorów w pikselu jest następujące:

b31 b30 b29 b28 b27 b26 b25 b24 b23 b22 b21 b20 b19 b18 b17 b16 b15 b14 b13 b12 b11 b10 b9 b8 b7 b6 b5 b4 b3 b2 b1 b0
A B G R
Ashift = 24 Bshift = 16 Gshift = 8 Rshift = 0

xmask – określa maskę bitową, która pozwala wydzielić z kodu piksela bity składowej koloru. Dla powyższego przykładu maski są następujące:

Rmask = 00000000000000000000000011111111 = 0x000000ff
Gmask = 00000000000000001111111100000000 = 0x0000ff00
Bmask = 00000000111111110000000000000000 = 0x00ff0000
Amask = 11111111000000000000000000000000 = 0xff000000

colorkey – kolor przezroczysty
alpha – przezroczystość całej powierzchni

Kolejne dwie struktury odnoszą się do obrazów. Przez obraz rozumiemy tutaj dowolną grafikę zbudowaną z pikseli.

SDL_Surface struktura definiuje obraz w pamięci RAM, do którego ma dostęp mikroprocesor. Biblioteka SDL2 została zoptymalizowana pod katem współpracy z akceleratorami graficznymi, które posiadają własną pamięć, niezależną od pamięci głównej komputera. Dlatego struktura SDL_Surface ma zastosowanie jedynie wtedy, gdy chcemy z poziomu programu uzyskać dostęp do pikseli obrazu.
struct SDL_Surface
{
  Uint32 flags;
  SDL_PixelFormat* format;
  int w, h;
  int pitch;
  void * pixels;
  void * userdata;
  int locked;
  void * lock_data;
  SDL_Rect clip_rect;
  SDL_BlitMap * map;
  int refcount;
};

flags – znaczniki używane wewnętrznie przez funkcje SDL2. Określają różne własności powierzchni.
format – wskaźnik struktury SDL_PixelFormat, która definiuje format pikseli tworzących obraz. Pole tylko do odczytu.
w – szerokość obrazu w pikselach. Tylko do odczytu.
h – wysokość obrazu w pikselach. Tylko do odczytu.
pitch – liczba bajtów na jedną linię pikseli obrazu.
pixels
– wskaźnik obszaru pamięci przechowującego piksele obrazu.
user data – wskaźnik danych użytkownika.
locked – używane wewnętrznie przy powierzchniach, które muszą być blokowane dla innych procesów.
lock_data – używane wewnętrznie przy powierzchniach, które muszą być blokowane dla innych procesów.
clip_rect – prostokąt określający aktywny obszar powierzchni. Modyfikacje będą się ograniczały jedynie do tego prostokąta.
map – wskaźnik używany wewnętrznie.
refcount – licznik odwołań. Używane przy usuwaniu powierzchni z pamięci.

SDL_Texture struktura definiuje obraz w pamięci VRAM akceleratora. Do danych bezpośredni dostęp ma jedynie akcelerator. Ten rodzaj danych jest najczęściej wykorzystywany w SDL2. Obraz w teksturze jest zoptymalizowany pod kątem wykorzystywania go przez akcelerator.  Dane zawarte w strukturze są specyficzne dla akceleratora i program użytkowy zwykle nie musi mieć do nich dostępu, a nawet nie powinien.
struct SDL_Texture
{
    const void *magic;
    Uint32 format;
    int access;
    int w;
    int h;
    int modMode;
    SDL_BlendMode blendMode;
    Uint8 r, g, b, a;
    SDL_Renderer *renderer;
    SDL_Texture *native;
    SDL_SW_YUVTexture *yuv;
    void *pixels;
    int pitch;
    SDL_Rect locked_rect;
    void *driverdata;
    SDL_Texture *prev;
    SDL_Texture *next;
};

Ostatnią strukturą, o której tutaj wspomnimy, jest struktura SDL_Renderer przechowująca informacje o sposobie tworzenia grafiki na teksturze. Angielskie słowo renderer oznacza w tym kontekście obiekt tworzący, rysujący grafikę. W strukturze SD_Renderer biblioteka SDL2 umieszcza wszystkie niezbędne informacje wykorzystywane przez funkcje graficzne operujące na teksturach, czyli obrazach w pamięci akceleratora graficznego. W systemie Windows nazywane jest to kontekstem graficznym. Definicja tej struktury jest dosyć skomplikowana, lecz program użytkownika nie musi mieć bezpośredniego dostępu do jej pól.

SDL_Renderer struktura definiuje parametry tworzenia grafiki.
struct SDL_Renderer
{
  const void *magic;
  void (*WindowEvent) (SDL_Renderer * renderer, const SDL_WindowEvent * event);
  int (*CreateTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
  int (*SetTextureColorMod) (SDL_Renderer * renderer,
                             SDL_Texture * texture);
  int (*SetTextureAlphaMod) (SDL_Renderer * renderer,
                             SDL_Texture * texture);
  int (*SetTextureBlendMode) (SDL_Renderer * renderer,
                              SDL_Texture * texture);
  int (*UpdateTexture) (SDL_Renderer * renderer, SDL_Texture * texture,
                        const SDL_Rect * rect, const void * pixels,
                        int pitch);
  int (*LockTexture) (SDL_Renderer * renderer, SDL_Texture * texture,
                      const SDL_Rect * rect, void **pixels, int * pitch);
  void (*UnlockTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
  int (*SetRenderTarget) (SDL_Renderer * renderer, SDL_Texture * texture);
  int (*UpdateViewport) (SDL_Renderer * renderer);
  int (*RenderClear) (SDL_Renderer * renderer);
  int (*RenderDrawPoints) (SDL_Renderer * renderer, const SDL_FPoint * points,
                           int count);
  int (*RenderDrawLines) (SDL_Renderer * renderer, const SDL_FPoint * points,
                          int count);
  int (*RenderFillRects) (SDL_Renderer * renderer, const SDL_FRect * rects,
                          int count);
  int (*RenderCopy) (SDL_Renderer * renderer, SDL_Texture * texture,
                     const SDL_Rect * srcrect, const SDL_FRect * dstrect);
  int (*RenderCopyEx) (SDL_Renderer * renderer, SDL_Texture * texture,
                     const SDL_Rect * srcquad, const SDL_FRect * dstrect,
                     const double angle, const SDL_FPoint * center,
                     const SDL_RendererFlip flip);
  int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect,
                           Uint32 format, void * pixels, int pitch);
  void (*RenderPresent) (SDL_Renderer * renderer);
  void (*DestroyTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
  void (*DestroyRenderer) (SDL_Renderer * renderer);
  int (*GL_BindTexture) (SDL_Renderer * renderer, SDL_Texture * texture,
                         float * texw, float * texh);
  int (*GL_UnbindTexture) (SDL_Renderer * renderer, SDL_Texture * texture);
  SDL_RendererInfo info;
  SDL_Window *window;
  SDL_bool hidden;
  SDL_bool resized;
  int logical_w;
  int logical_h;
  int logical_w_backup;
  int logical_h_backup;
  SDL_Rect viewport;
  SDL_Rect viewport_backup;
  SDL_FPoint scale;
  SDL_FPoint scale_backup;
  SDL_Texture *textures;
  SDL_Texture *target;
  Uint8 r, g, b, a;
  SDL_BlendMode blendMode;
  void *driverdata;
};

Zapamiętaj:

SDL_Surface definiuje obraz w pamięci RAM do przetwarzania za pomocą mikroprocesora bez wykorzystywania akceleratora graficznego.

SDL_Texture definiuje obraz w pamięci VRAM do przetwarzania za pomocą akceleratora.

SDL_Renderer definiuje parametry rysowania obrazu przez akcelerator.


Na początek:  podrozdziału   strony 

Programy

Gdy poznałeś już podstawowe struktury biblioteki SDL2, postarajmy się napisać kilka prostych programów, które zilustrują ich wykorzystanie.

Uruchom CodeBlocks i utwórz z szablonu nowy projekt sdl2.

Pierwszy program pokazuje, jak programowo można tworzyć grafikę przy wykorzystaniu obrazu w pamięci RAM. Przekopiuj do edytora poniższy program, skompiluj go i uruchom (w Linuxie użyj dyrektywy: #include <SDL2/SDL.h>):

C++
// Grafika programowa
//----------------------

#include <SDL.h>
#include <iostream>

using namespace std;

int main(int argc, char * args[])
{
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tutaj umieszczamy kod dla SDL2

  SDL_Window * w = SDL_CreateWindow("Grafika", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 480, 640, 0);

  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 1;
  }

  // Powierzchnia graficzna okna
  SDL_Surface * s = SDL_GetWindowSurface(w);

  // Blokujemy dostęp do powierzchni graficznej innym procesom
  SDL_LockSurface(s);

  // Kolejne linie obrazu wypełniamy stopniami szarości
  for(int i = 0; i < s->h; i++) SDL_memset((Uint8 *) s->pixels+i*s->pitch,i+i,s->pitch);

  // Odblokowujemy dostęp do powierzchni
  SDL_UnlockSurface(s);

  // Uaktualniamy zawartość okna
  SDL_UpdateWindowSurface(w);

  // Czekamy 5 sekund
  SDL_Delay(5000);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Koniec pracy z SDL2
  SDL_Quit();

  return 0;
}

Początek i koniec programu jest taki sam, jak w programie z poprzedniego rozdziału.

if(SDL_Init(...))... Inicjujemy bibliotekę SDL2
SDL_Window * w = SDL_CreateWindow(...) Tworzymy okno o wybranych parametrach. Wskaźnik w wskazuje strukturę okna.
SDL_Surface * s = SDL_GetWindowSurface(w) Tworzymy kopię obszaru okna w pamięci RAM. Dostęp do powierzchni mamy poprzez wskaźnik s.
SDL_LockSurface(s) Blokujemy dostęp do utworzonej powierzchni. Nie wszystkie powierzchnie muszą być blokowane. Zrobiłem to tutaj tak na wszelki wypadek.
for(int i = 0; i < s->h; i++) ... Pętla wykonuje się tyle razy, ile linii zawiera powierzchnia graficzna okna. W zmiennej i mamy numer linii obrazu.
SDL_memset(...) Funkcja ustawia bajty w obszarze pamięci na określoną wartość. Wymaga trzech parametrów:
SDL_memset(a,v,n)
a – adres obszaru
v – wartość, którą zostaną wypełnione bajty w obszarze
n – liczba bajtów w obszarze
SDL_memset((Uint8 *) s->pixels+i*s->pitch, ...) Adres obszaru do wypełnienia pobieramy ze struktury SDL_Surface wskazywanej przez wskaźnik s. Do adresu dodajemy przesunięcie: numer linii x liczba bajtów w linii. W ten sposób otrzymamy adres początku linii o zadanym numerze, tutaj o numerze i. W języku C++ adresy mają typy zgodne z typami adresowanych obiektów. Dlatego wynik dodawania do adresu przesunięcia rzutujemy na typ 8-bitowy bez znaku, czyli będzie to adres wskazujący obszar zawierający bajty.
SDL_memset(..., i+i, ...) To jest wartość, którą zostaną ustawione bajty obszaru. Bajty w linii obrazu o numerze 0 zostaną ustawione na 0, w linii o numerze 1 na 2, w linii o numerze 2 na 4, itd. W efekcie kolor kolejnych linii będzie coraz jaśniejszym odcieniem szarości (dlaczego akurat szarości?). Gdy i + i przekroczy 254, to następną wartością będzie 0, ponieważ bajt może zapamiętać tylko 8 bitów, zatem z 256 dostaniemy 0 po odrzuceniu najstarszego dziewiątego bitu (256 dwójkowo to 1000000002), z 258 dostaniemy 2 (258 dwójkowo to 1000000102), itd.
SDL_memset(..., s->pitch) Ten parametr określa długość linii obrazu w bajtach, a dla funkcji SDL_memset() oznacza wielkość obszaru pamięci do wypełnienia zadaną wartością
SDL_UnlockSurface(s) Odblokowujemy powierzchnię graficzną
SDL_UpdateWindowSurface(w) Aby treść obrazu pojawiła się w oknie, należy przesłać powierzchnię z pamięci RAM do pamięci VRAM karty graficznej i umieścić ją w buforze ekranu. Tę operację realizuje funkcja SDL_UpdateWindowSurface(). Po jej wykonaniu w oknie pojawi się obraz, który program stworzył na powierzchni graficznej.

Reszta programu jest taka sama jak w przykładzie z poprzedniego rozdziału, więc nie muszę powtarzać opisu.

Program pokazuje sposób pracy z powierzchnią graficzną okna. Zasada jest następująca:

  1. Z okna pobierasz do RAM powierzchnię graficzną za pomocą funkcji SDL_GetWindowSurface().
  2. Powierzchnię rezerwujesz za pomocą funkcji SDL_LockSurface().
  3. Zmieniasz w dowolny sposób zawartość obszaru pikseli.
  4. Powierzchnię odblokowujesz za pomocą funkcji SDL_UnlockSurface().
  5. Uaktualnisz w VRAM treść okna za pomocą funkcji SDL_UpdateWindowSurface().

Tworzenie grafiki za pomocą mikroprocesora jest niestety wolne. Dlatego opracowano układy wspomagające, czyli akceleratory grafiki. Logicznie są one dużo prostsze niż procesor, lecz potrafią wykonywać jednocześnie tysiące operacji. W rezultacie grafika tworzona jest setki razy szybciej, niż potrafiłby to zrobić mikroprocesor. Biblioteka SDL2 współpracuje intensywnie z akceleratorami i preferowanym sposobem tworzenia grafiki jest wykorzystanie akceleratora. Dlatego strukturę SDL_Surface wykorzystuje się tylko w niektórych przypadkach, np. przy odczycie obrazków z plików, gdzie akcelerator nie poradzi. Częściej wykorzystywane będą dwie struktury: SDL_Texture do definicji obrazu w pamięci VRAM oraz SDL_Renderer do tworzenia grafiki na teksturach w VRAM.

Następny program wykonuje to samo zadanie, jednak za pomocą akceleratora grafiki z wykorzystaniem kontekstu graficznego, czyli struktury SDL_Renderer.

Skopiuj do edytora poniższy program, skompiluj i uruchom go (w Linuxie użyj dyrektywy: #include <SDL2/SDL.h>):

C++
// Grafika sprzętowa
//------------------

#include <SDL.h>
#include <iostream>

using namespace std;

const int W_W = 480;
const int W_H = 640;

int main(int argc, char * args[])
{
  if(SDL_Init(SDL_INIT_VIDEO))
  {
    cout << "SDL_Init Error: " << SDL_GetError() << endl;
    return 1;
  }

  // Tutaj umieszczamy kod dla SDL2

  SDL_Window * w = SDL_CreateWindow("Grafika", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0);

  if(!w)
  {
    cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl;
    SDL_Quit();
    return 2;
  }

  // Tworzymy strukturę SDL_Renderer do operacji graficznych wykonywanych za pomocą akceleratora
  SDL_Renderer * r = SDL_CreateRenderer(w, 0, 0);

  if(!r)
  {
     cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl;
     SDL_DestroyWindow(w);
     SDL_Quit();
     return 3;
  }

  // Rysujemy grafikę

  for(int i = 0; i < W_H; i++)
  {
      SDL_SetRenderDrawColor(r,i+i,i+i,i+i,255);
      SDL_RenderDrawLine(r,0,i,W_W-1,i);
  }

  // Uaktualniamy obraz
  SDL_RenderPresent(r);

  // Czekamy 5 sekund
  SDL_Delay(5000);

  // Usuwamy kontekst graficzny
  SDL_DestroyRenderer(r);

  // Usuwamy okno
  SDL_DestroyWindow(w);

  // Koniec pracy z SDL2
  SDL_Quit();

  return 0;
}

Opiszmy poszczególne elementy składowe programu:

if(SDL_Init(...))... Inicjujemy bibliotekę SDL2
SDL_Window * w = SDL_CreateWindow(...) Tworzymy okno o wybranych parametrach. Wskaźnik w wskazuje strukturę okna.
SDL_Renderer * r = SDL_CreateRenderer(...) Tworzymy kontekst graficzny. We wskaźniku r zapamiętujemy adres struktury SDL_Renderer. Funkcja SDL_CreateRenderer() tworzy i wypełnia tę strukturę na podstawie struktury SDL_Window, której adres otrzymuje jako parametr. Dodatkowo w pamięci VRAM akceleratora tworzona jest tekstura z treścią okna.
SDL_CreateRenderer(w,n,f)

w – wskaźnik struktury SDL_Window, która opisuje okno
n – numer sterownika grafiki, który zostanie użyty do operacji w oknie. 0 oznacza standardowy sterownik.
f – znaczniki. Opiszemy je dalej w kursie. 0 oznacza grafika przyspieszana sprzętowo.

if(!r) ... Jeśli adres zwrócony przez funkcję SDL_CreateRenderer() jest adresem zerowym (w języku C++ adres zerowy nie wskazuje żadnego obiektu w pamięci), to wypisujemy odpowiedni komunikat, usuwamy okno, zamykamy bibliotekę SDL2 i kończymy program z kodem 2.
for(int i = 0; i < W_H; i++) Pętla rysująca kolejne linie obrazu. Wykonuje się tyle razy, ile linii zawiera okno (W_H jest stałą równą wysokości okna w liniach).
SDL_SetRenderDrawColor(...) Funkcja ustawia kolor do rysowania. Posiada następujące parametry:
SDL_SetRenderDrawColor(rnd,r,g,b,a)
rnd – wskaźnik struktury SDL_Renderer z kontekstem graficznym
r – składowa czerwona koloru
g – składowa zielona koloru
b – składowa niebieska koloru
a – nieprzezroczystość, tzw. kanał alfa. Wartość 255 oznacza pełną nieprzezroczystość

Poeksperymentuj w programie z kolorami składowymi r, g i b, a otrzymasz różne ciekawe gradienty. Na przykład podmień w programie wywołanie tej funkcji na takie:

SSDL_SetRenderDrawColor(r,i*8,255-i/3,0,255);
SDL_RenderDrawLine(...) Funkcja rysuje linię w ustawionym wcześniej kolorze. Parametry są następujące:
SDL_RenderDrawLine(r,xp,yp,xk,yk)
r – wskaźnik struktury SDL_Renderer
xp,yp – współrzędne punktu początkowego linii
xk,yk – współrzędne punktu końcowego linii

W programie rysujemy linię poziomą na kolejnych liniach obrazu okna. Linia rysowana jest na szerokość całego okna (W_W jest stałą równą szerokości okna).

SDL_RenderPresent(...) Grafika rysowana jest na teksturze w pamięci karty graficznej. Nie pojawia się na ekranie monitora, dopóki nie prześlemy tekstury do bufora obrazu kart. Ta funkcja wykonuje tę właśnie operację, uaktualnia z tekstury bufor ekranowy. Do przesłania danych wykorzystuje się akcelerator, zatem operacja wykonana zostanie błyskawicznie. Parametrem funkcji jest wskaźnik struktury SDL_Renderer.
SDL_DestroyRenderer(...) Funkcja usuwa kontekst graficzny. Używamy jej, gdy już skończymy pracę z oknem. Wywołanie jest ważne, ponieważ kontekst graficzny rezerwuje w VRAM teksturę na treść okna. Przy usuwaniu kontekstu tekstura również zostanie usunięta.

Reszta programu jest taka sama jak w poprzednich przykładach. Usuwamy okno, zamykamy SDL2 i kończymy poleceniem return 0.


Na początek:  podrozdziału   strony 

Podsumowanie

SDL_GetWindowSurface(w) – tworzy powierzchnię graficzną okna w pamięci głównej RAM i zwraca wskaźnik do opisującej ją struktury SDL_Surface. Tak utworzona powierzchnia jest dostępna dla mikroprocesora, który programowo może modyfikować jej zawartość.

w – wskaźnik struktury SDL_Window

SDL_LockSurface(s) – blokuje dostęp do powierzchni graficznej okna innym procesom.

s – wskaźnik struktury SDL_Surface

SDL_Memset(a,v,n) – ustawia bajty zadanego obszaru na podaną wartość.

a – adres obszaru
v – wartość, którą zostaną wypełnione bajty w obszarze
n – liczba bajtów w obszarze

SDL_UnlockSurface(s) – odblokowuje dostęp do powierzchni graficznej.

s – wskaźnik struktury SDL_Surface

SDL_UpdateWindowSurface(w) – uaktualnia bufor ekranu w VRAM treścią powierzchni graficznej. Innymi słowy wyświetla w okienku zawartość tej powierzchni.

w – wskaźnik struktury SDL_Window

SDL_CreateRenderer(w,n,f) – tworzy dla okna kontekst graficzny (strukturę SDL_Renderer), który jest później wykorzystywany do wykonywania operacji graficznych w SDL2. W pamięci VRAM przydziela dla tego kontekstu odpowiednią teksturę. Jako wynik zwraca wskaźnik do struktury SDL_Renderer.

w – wskaźnik struktury SDL_Window, która opisuje okno
n – numer sterownika grafiki, który zostanie użyty do operacji w oknie. 0 oznacza standardowy sterownik.
f – znaczniki

SDL_SetRenderDrawColor(r,cr,cg,cb,ca) – ustawia kolor dla operacji graficznej.

r – wskaźnik struktury SDL_Renderer
cr – składowa czerwona
cg – składowa zielona
cb – składowa niebieska
ca – przezroczystość koloru

SDL_RenderDrawLine(r,x1,y1,x2,y2) – rysuje odcinek

r – wskaźnik struktury SDL_Renderer
x1,y1 – współrzędne punktu początkowego odcinka
x2,y2 – współrzędne punktu końcowego odcinka

SDL_RenderPresent(r) – przesyła teksturę przydzieloną kontekstowi graficznemu do bufora ekranu.

r – wskaźnik struktury SDL_Renderer

SDL_DestroyRenderer(r) – usuwa z pamięci kontekst graficzny wraz z przydzieloną mu teksturą w VRAM.

r – wskaźnik struktury SDL_Renderer


Na początek:  podrozdziału   strony 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2024 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.