Objavljeno: 24.10.2005 19:39 | Avtor: Dalibor Kranjčič | Monitor Marec 2004

Uporaba večpredstavnih knjižnic SDL

SDL (Simple DirectMedia Layer) je večpredstavna knjižnica, podobna DirectX, vendar jo je bistveno enostavneje uporabljati.

SDL podpira naslednje operacijske sisteme: Linux, Windows, BeOS, MacOS Classic, MacOS X, FreeBSD, OpenBSD, BSD/OS, Solaris, IRIX, in QNX. Knjižnica SDL je izvirno napisana v programskem jeziku C/C++. Obstaja pa povezava z drugimi programskimi jeziki, med drugim: ada, eiffel, java, lua, ML, perl, PHP, pike, python in ruby. Če obvladamo osnove programskega jezika C, SDL ponuja preprosto in zabavno izdelovanje večpredstavnih uporabniških programov in igric.

Namestitev (Linux)

Najnovejša različica knjižnic SDL je na spletni strani http://www.libsdl.org/download-1.2.php. Na voljo so izdaje, ki podpirajo različne operacijske sisteme in arhitekture računalnikov.

Prenesite paket z izvirno kodo SDL-1.2.6.tar.gz in vpišite naslednje zaporedje ukazov v lupini:

# tar xzvf SDL-1.2.6.tar.gz

# ./configure ; make ; make install

SDL in programski jezik C/C++

Za dostop do funkcij SDL v programskem jeziku C/C++ v izvirno kodo vključujemo datoteko SDL.h (predprocesorski ukaz #include).

Inicializacijo knjižnic izvedemo s funkcijo SDL_Init, ki določa, katere namenske sklope funkcij bomo uporabili v kodi. Možno je upravljanje različnih naprav: zaslona, zvočne kartice, cd-roma, igralne palice, miške in tipkovnice.

V spodnjem zgledu izvajamo inicializacijo zaslona in zvoka:

if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) < 0) {

printf ("Napaka: ne morem inicializirati SDL: %s\n", SDL_GetError());

exit(1);

}

Pri zaključevanju programa funkcija atexit() kliče funkcijo SDL_Quit. Ta funkcija na novo nastavi vse video načine in briše prejšnje nastavitve knjižnic SDL.

V nadaljevanju bomo podali opis osnovnih pojmov in funkcij ter zglede rabe knjižnic SDL v programskem jeziku C/C++.

Površine

Knjižnice SDL uporabljajo pojem površin (surfaces) za predstavitev področij grafičnega pomnilnika. Površina je, na primer, zaslon ali slika BMP. Površine SDL so deklarirane kot kazalec na "grafično" strukturo SDL_Surface.

Preden začnemo risati, s funkcijo SDL_SetVideoMode pripravimo površino za risanje (zaslon). Funkcija jemlje naslednje parametre: širino in višino grafične ločljivosti, bitno globino (bitdepth) in grafične možnosti.

Na voljo so naslednje grafične možnosti:

  • SDL_SWSURFACE - Ustvari video površino v sistemskem pomnilniku.
  • SDL_HWSURFACE - Ustvari video površino v video pomnilniku.
  • SDL_ANYFORMAT - Dovoljuje ustvarjanje površine poljubne bitne globine.
  • SDL_HWPALETE - Omogoča popoln dostop do barvne lestvice video pomnilnika.
  • SDL_DOUBLEBUF - Uporabi dvojno pomnjenje (double buffering) površine. Program najprej ustvari vse risane predmete v dvojnem medpomnilniku, nato pa kopira celotno vsebino v video pomnilnik.
  • SDL_FULLSCREEN - Prikaže celoten zaslon.
  • SDL_OPENGL - Ustvari površino tipa OpenGL.
  • SDL_RESIZABLE - Ustvari okno z možnostjo spreminjanja velikosti.
  • SDL_NOFRAME - Ustvari okno brez okvira.
  • Delo s površinami

    SDL ponuja precejšnji nabor funkcij za delo z zaslonom in površinami. Podali bomo nekaj osnovnih:

    Funkcija SDL_LoadBMP naloži površino, podano z imenom datoteke BMP, kot lasten argument. Vrnjena vrednost funkcije je kazalec na strukturo SDL_Surface, ki bo enolično predstavljal naloženo datoteko BMP v drugih funkcijah za delo s površinami.

    SDL_Surface *p_znakec;

    p_znakec = SDL_LoadBMP("znakec.bmp");

    Površino je mogoče shraniti z uporabo funkcije SDL_SaveBMP.

    int SDL_SaveBMP(SDL_Surface *surface, const char *file);

    Funkcija jemlje dva argumenta: kazalec na površino in ime datoteke BMP, v katero bo površina shranjena. Funkcija vrne vrednost 0, če je bilo shranjevanje uspešno, oziroma -1, če se je zgodila napaka.

    Za risanje (kopiranje) površine na drugo površino uporabljamo funkcijo SDL_BlitSurface.

    SDL_BlitSurface(p_slika, NULL, zaslon, &koordinate);

    Prvi argument funkcije predstavlja površino, ki bo izrisana na površini, določeni s tretjim argumentom funkcije. Položaj kopirane površine na ciljni površini predstavlja četrti argument funkcije, ki je kazalec na strukturo SDL_Rect. Dejanski položaj določata člena x in y te strukture:

    typedef struct{

    Sint16 x, y;

    Uint16 w, h;

    } SDL_Rect;

    V programskem zgledu, ki ga bomo podali v nadaljevanju, uporabljamo funkcijo RisiSlikoNaZaslon za izrisovanje površin na zaslonu.

    // Funkcija riše sliko na zaslon

    void RisiSlikoNaZaslon(SDL_Surface *p_slika, int n_x_poz, int n_y_poz, SDL_Surface* &zaslon)

    {

    SDL_Rect koordinate;

    koordinate.x = n_x_poz;

    koordinate.y = n_y_poz;

    SDL_FillRect(zaslon, NULL, 2000);

    SDL_BlitSurface(p_slika, NULL, zaslon, &koordinate);

    SDL_Flip(zaslon);

    }

    Funkcija SDL_FillRect() izpolni zaslon z modro barvo (vrednost 1000). Funkcijo SDL_Flip() uporabljamo za sinhronizacijo podatkov risane površine z zaslonom.

    Obdelava dogodkov

    Obdelava dogodkov (event handling) omogoča uporabniškemu programu sprejemanje uporabniških vhodnih podatkov iz tipkovnice, miške ali igralne palice.

    Dogodke inicializiramo s klicem funkcije SDL_Init.

    SDL_Init(SDL_INIT_VIDEO);

    SDL shranjuje vse dogodke, ki čakajo na obdelavo, v dogodkovno vrsto (event queue). Branje dogodkov iz dogodkovne vrste izvajamo ob pomoči funkcije SDL_PollEvent. Funkcija jemlje kot argument kazalec na strukturo SDL_Event.

    Podali bomo enostaven zgled obdelave dogodkov. Ob pritisku na tipko Esc se bo sprožil dogodek, ki bo povzročil konec programa:

    Najprej deklariramo dogodek test_dogodka s pomočjo strukture SDL_Event:

    SDL_Event test_dogodka;

    Dogodek test_dogodka nastopa kot argument v funkciji SDL_PollEvent. Ta funkcija je hkrati argument while zanke, v kateri preverjamo tip novo nastalega dogodka:

    while ((SDL_PollEvent(&test_dogodka))) {

    if (test_dogodka.type == SDL_KEYDOWN)

    if (test_dogodka.key.keysym.sym == SDLK_ESCAPE) // Konec programa 

    exit(1);     

    }

    Tip dogodka je določen s členom type strukture SDL_Event. V naslednji vrstici preverjamo, ali je prišlo do pritiska ene od tipk na tipkovnici:

    if (test_dogodka.type == SDL_KEYDOWN)

    Temu sledi določanje pritisnjene tipke:

    if (test_dogodka.key.keysym.sym == SDLK_ESCAPE)

    Tipko Esc (Escape) predstavlja konstanta SDLK_ESCAPE.

    Podobno bi preverjali pritisk drugih tipk, na primer:

    puščica gor

    SDLK_UP

    puščica dol

    SDLK_DOWN

    puščica levo

    SDLK_LEFT

    puščica desno

    SDLK_RIGHT

    tipka q

    SDLK_q

    tipka r

    SDLK_r

    Na miški poznamo naslednje tipe dogodkov: SDL_MOUSEMOTION (premik miške) in SDL_MOUSEBUTTONDOWN/UP (pritisk tipke na miški).

    Tipi dogodkov na igralni palici so: SDL_JOYAXISMOTION, SDL_JOYAXISMOTION, SDL_JOYHATMOTION, SDL_JOYBUTTONDOWN/UP.

    Zgled rabe knjižnic SDL

    Podali bomo program, ki bo omogočal premikanje slike na zaslonu z usmerjevalnimi tipkami (puščice). Hkrati bo možno premeščati sliko na poljubno mesto s pritiskom na levo tipko miške. V ta namen potrebujemo poljubno sliko BMP, veliko 30 × 30 pikslov. Ker je barvna podlaga modre barve, izberite barvo slike, ki bo vidna na taki podlagi. Sliko shranite pod imenom znakec.bmp v isto mapo kakor program.

    // Datoteka SDL_zgled.c

    // Zgled rabe knjižnic SDL

    #include <stdio.h>

    #include <stdlib.h>

    #include <SDL/SDL.h>

    // Definiramo, za koliko se premakne slika

    // na zaslonu ob pritisku na tipko

    #define PREMIK 20

    // Deklaracija površin

    SDL_Surface *p_zaslon; // Zaslon

    SDL_Surface *p_znakec; // Slika

    // Funkcija riše sliko na zaslon

    void RisiSlikoNaZaslon(SDL_Surface *p_slika, int n_x_poz, int n_y_poz, SDL_Surface* &zaslon)

    {

    SDL_Rect koordinate;

    koordinate.x = n_x_poz;

    koordinate.y = n_y_poz;

    SDL_FillRect(zaslon, NULL, 2000);

    SDL_BlitSurface(p_slika, NULL, zaslon, &koordinate);

    SDL_Flip(zaslon);

    }

    /*** Glavni program ***/

    int main()

    {

    SDL_Event event;

    // Začetni položaj na zaslonu

    int n_x = 200;

    int n_y = 200;

    // Začetno stanje tipk

    bool b_tipka_dol = false;

    bool b_dol = false;

    bool b_gor = false;

    bool b_levo = false;

    bool b_desno = false;

    // Inicializacija knjižnic SDL

    if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO) < 0) {

    printf ("Napaka: ne morem inicializirati SDL: %s\n", SDL_GetError());

    exit(1);

    }

    atexit(SDL_Quit);

    // Priprava površine za risanje (zaslona)

    p_zaslon = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);

    // Preverjanje   

    if (p_zaslon == NULL) {

    printf("Ne morem inicializirati video signala na ločljivost 640 x 480: %s\n", SDL_GetError());

    exit(1);

    }

    // Kazalec na površino SDL povežemo z datoteko BMP

    p_znakec = SDL_LoadBMP("znakec.bmp");

    // Rišemo sliko na zaslon

    RisiSlikoNaZaslon(p_znakec, n_x, n_y, p_zaslon);

    // Preverjamo, ali so dogodki v dogodkovni vrsti

    while(true) {

    while ((SDL_PollEvent(&event)) || (b_tipka_dol)) {

    // Preverjamo, ali so bile tipke pritisnjene   

    if (event.type == SDL_KEYDOWN) {

    b_tipka_dol = true;

    switch (event.key.keysym.sym) {

    case SDLK_UP:

    n_y -= PREMIK;    

    b_gor = true;

    break;

    case SDLK_DOWN:

    n_y += PREMIK;

    b_dol = true;

    break;

    case SDLK_RIGHT:

    n_x += PREMIK;

    b_desno = true;

    break;

    case SDLK_LEFT:

    n_x -= PREMIK;

    b_levo = true;

    break;

    case SDLK_ESCAPE:

    exit(1);

        break;

    }

    // Preverjamo, ali so bile tipke sproščene   

    } else if (event.type == SDL_KEYUP) {

    b_tipka_dol = b_gor = b_dol = b_desno = b_levo = false;  

    break;

    // Preverjamo pritisk tipke na miški

    } else if (event.type == SDL_MOUSEBUTTONDOWN) {

    RisiSlikoNaZaslon(p_znakec, event.button.x, event.button.y, p_zaslon);

    n_x = event.button.x;

    n_y = event.button.y;

    }  

    // Premikamo in rišemo sliko, dokler je tipka pritisnjena

    if (b_gor)

    n_y -= PREMIK;

    else if (b_dol)

    n_y += PREMIK;

    else if (b_desno)

    n_x += PREMIK;

    else if (b_levo)

    n_x -= PREMIK;

    if (b_tipka_dol)

    RisiSlikoNaZaslon(p_znakec, n_x, n_y, p_zaslon);

    }

    }

    return 0;

    }

    Program prevedemo in poženemo z naslednjim zaporedjem ukazov v lupini:

    # g++ -c `sdl-config --cflags` SDL_zgled.c

    # g++ -o SDL_zgled SDL_zgled.o `sdl-config --libs` -lSDL_image

    # ./SDL_zgled

    Ukazovanje cd-romu

    Za konec še posladek. Knjižnice SDL omogočajo analizo in predvajanje zvočnih zapisov na cd-romu, neposredno iz programskega jezika C/C++.

    Sledi zgled, ki bo prikazal vsebino cd-roma in nato predvajal posamezne skladbe:

    // Datoteka: cdrom.c

    // SDL knjižnice: primer upravljanja naprave CD-ROM

    #include <stdio.h>

    #include <stdlib.h>

    #include <SDL/SDL.h>

    int main ()

    {

    SDL_CD *cdrom; // Deklariramo napravo CD-ROM

    CDstatus status;

    int n_st, n_m, n_s, n_f;

    // Najprej inicializiramo SDL za upravljanje naprave CR-ROM

    if (SDL_Init(SDL_INIT_CDROM) < 0 ) {

    fprintf(stderr, "Napaka pri inicializaciji SDL: %s\n",SDL_GetError());

    exit(1);

    }

    // Izhod

    atexit(SDL_Quit);

    // Odpri privzeto napravo CD-ROM      

    cdrom = SDL_CDOpen(0);

    // Preveri, ali je bilo odpiranje uspešno

    if (cdrom == NULL) {

    fprintf(stderr, "Ne morem odpreti privzete naprave CD-ROM: %s\n",

    SDL_GetError());

    exit(2);

    }

    // Status naprave CR-ROM     

    SDL_CDStatus(cdrom);

    printf("Skladbe: %d\n", cdrom->numtracks);

    for (n_st = 0; n_st < cdrom->numtracks; ++n_st) {

    FRAMES_TO_MSF(cdrom->track[n_st].length, &n_m, &n_s, &n_f);

    if (n_m > 0)

    ++n_m;

    printf("\tSkladba (index %d) %d: %d:%2.2d\n", n_st,

    cdrom->track[n_st].id, n_m, n_s);

    }

    // Predvajaj celotni CD

    //if (CD_INDRIVE(SDL_CDStatus(cdrom)) )

    // SDL_CDPlayTracks(cdrom, 0, 0, 0, 0);

    // Predvajaj zadnjo skladbo

    //if (CD_INDRIVE(SDL_CDStatus(cdrom)) ) {

    // SDL_CDPlayTracks(cdrom, cdrom->numtracks-1, 0, 0, 0);

    //}

    // Predvajaj 10 s prve skladbe

    if ( CD_INDRIVE(SDL_CDStatus(cdrom)) )

    SDL_CDPlayTracks(cdrom, 0, 0, 0, CD_FPS * 10);

    }

    Program prevedemo in poženemo z naslednjim zaporedjem ukazov v lupini:

    # g++ -c `sdl-config --cflags` cdrom.c

    # g++ -o cdrom cdrom.o `sdl-config --libs` -lSDL_image

    # ./cdrom

    Še več podatkov o uporabi knjižnic SDL najdete na spletni strani http://www.libsdl.org/index.php.

    Naroči se na redna tedenska ali mesečna obvestila o novih prispevkih na naši spletni strani!

    Komentirajo lahko le prijavljeni uporabniki

     
    • Polja označena z * je potrebno obvezno izpolniti
    • Pošlji