Kirjoittaja: Heikki (2004).
⚠ Huomio! Tämä opas on vanhentunut. Oppaan sisältöön ei voi enää luottaa. Opas on säilytetty vain sen historiallisen arvon vuoksi. ⚠
Opassarjan ensimmäisessä osassa emme tehneet juuri mitään, mutta nyt alamme jo pääsemään asiaan! Tässä osassa käymme lävitse grafiikkaan liittyvät toimenpiteet.
Bittikartat ovat pakkaamattomia kuvatiedostoja (pääte .bmp). Bittikarttoja varten SDL:ässä on valmis latausfunktio, SDL_LoadBMP
, joka lataa kuvatiedoston kuvapinnalle joka on tyyppiä SDL_Surface
. Tähän mennessä olet nähnyt SDL_Surface:n
ensimmäisen osan esimerkkikoodissa, jossa kuvapinnalle laitetaan näytön sisältö:
SDL_Surface *naytto; naytto = SDL_SetVideoMode(1024, 768, 32, SDL_HWSURFACE|SDL_FULLSCREEN); //nyt naytto sisältää näytöllä olevan kuvan
Mutta kuten jo sanoin, kuvapinnoilla voi olla muutakin kuin näytön sisältö. Tässä tapauksessa tallennamme siihen bittikartan sisällön. Eli tutustutaanpas nyt SDL_LoadBMP
-funktioon:
SDL_Surface *kuva; kuva = SDL_LoadBMP("kuvatiedosto.bmp");
Ja katsos, tämähän on yksinkertaista! Ainoa huomionarvoisa asia on se, että meidän on käytettävä kuvapinnan osoitinta. Jos siis korvaisit rivin SDL_Surface *kuva;
rivillä SDL_Surface kuva;
koodi ei toimisi.
SDL_LoadBMP() varaa tarvittavan muistin, johon SDL_Surface-tyyppinen kuva-osoitin osoittaa. Tämä muisti pitää vapauttaa jahka kuvapintaa ei enää tarvita, viimeistään ohjelman lopussa. Vapautus hoituu funktiolla SDL_FreeSurface:
SDL_FreeSurface(kuva);
Mutta entäs sitten? Se, että meillä on kuva muistissa, ei paljon lohduta. Joten nyt täytyy piirtää se näytölle! Ja mitenkäs se sitten tapahtuu? No, lue pari riviä alaspäin niin opit!
SDL:llä on mahdollista käsitellä ruutua kahdella tavalla: kaksoispuskuroinnilla ja ilman. Se, käytätkö kaksoispuskurointia, vaikuttaa piirtotapaan. Kaksoispuskurointia käytetään yleensä peliohjelmoinnissa, ja sen avulla ehkäistään välkkymistä, ja piirtelykin sujuu nopeasti.
Alustettaessa näyttöä SDL_SetVideoMode
-funktiolla kerrotaan, käytetäänkö kaksoispuskurointia vai ei. Kaksoispuskurointi otetaan käyttöön lipulla SDL_DOUBLEBUF
. Ilman mainittua lippua kaksoispuskurointia ei käytetä. Tässä esimerkkinä ikkunan luonti sekä kaksoispuskuroinnilla että ilman:
SDL_Surface *naytto; // ei kaksoispuskurointia: naytto = SDL_SetVideoMode(1024, 768, 32, SDL_HWSURFACE|SDL_FULLSCREEN); // kaksoispuskurointi naytto = SDL_SetVideoMode(1024, 768, 32, SDL_HWSURFACE|SDL_FULLSCREEN|SDL_DOUBLEBUF);
Kaikkien lippujen selitykset löytyvät oppaan ensimmäisestä osasta.
Nyt meillä on sitten kaksoispuskurointi päällä tai poissa. On aika piirtää kuvapinnalle kuva, joten tehdäänpä funktio joka piirtää ruudulle kuvan.
// funktio piirtää parametrina annetun kuvan ruudulle (toinen parametri) tiettyyn kohtaan (3. ja 4. parametri) void PiirraKuva(SDL_Surface *kuva, SDL_Surface *naytto, int x, int y) { SDL_Rect alue; // mille alueellä näyttöä kuva piirretään alue.x = x; // koordinaatit alue.y = y; SDL_BlitSurface(kuva, NULL, naytto, &alue); // piirto }
Tätä funktiota voitasiin kutsua esimerkiksi näin:
SDL_Surface *naytto; SDL_Surface *kuva; naytto = SDL_SetVideoMode(1024, 768, 30, SDL_HWSURFACE|SDL_DOUBLEBUF); kuva = SDL_LoadBMP("kuva.bmp"); // ruudulle: PiirraKuva(kuva, naytto, 50, 50); // kuvan vapautus: SDL_FreeSurface(kuva);
Tämä piirtäisi bittikartan kuva.bmp sisällön ruudulle kohtaan 50,50. Mutta kun käännät tämän koodin, huomaat yhden asian: ruutu pysyy tyhjänä! Bugi? Surkea kirjasto? Ei kumpikaan, sinulta puuttuu vielä yksi tärkeä komento: SDL_Flip(naytto);
joka ns. flippaa kuvapinnat (tästä lisää kohta). Kokeileppa kirjoittaa tuo komento funktiokutsun jälkeen ja kääntää koodi. Ja tadaa... kaikki toimii! Kuva näkyy! Jos taas et käytä kaksoispuskurointia, on käytettävä toista funktiota, joka esitellään myöhemmin tässä oppaassa.
Kannattaa laittaa koodin loppuun funktiokutsu SDL_Delay(5000)
joka odottaa tietyn ajan, esim. 5000 ms (5 sekuntia). Muuten ruutu vain vilahtaa näkyvissä.
Miksi tämä sitten tarvitaan? Selostan tähän väliin hieman kaksoispuskuroinnin teoriaa: Kaksoispuskuroinnissa on nimen mukaisesti kaksi kuvapintaa (ns. framea): toinen on näytön sisältö, ja toisessa on seuraava piirrettävä kuva, johon kaikki piirto-operaatiot kohdistetaan. Kun kuva on piirretty, se on ainoastaan näkymättömällä kuvapinnalla. Se saadaan näkyviin vaihtamalla näkyvää kuvapintaa (ns. flippaus). SDL:ssä tämä hoituu SDL_Flip
-funktiolla. Eikö tämä sitten hidasta ohjelmaa? Ei, sillä funktio ainoastaan vaihtaa näytöllä näkyvän ja näkymättömän kuvapinnan osoitteita muistissa. Ja tadaa, seuraavan kerran kun näytönohjain piirtää jotain, tulee ruudulle uusi kuva. Nyt kuva ei välky, sillä kaikki piirretään ruudulle "kerralla".
Entä sitten ilman kaksoispuskurointia? Sekin on mahdollista, ja joissain harvoissa tapauksissa jopa hyödyllistä. Jos muutokset koskevat vain tiettyä osaa ruudusta, saadaan yksoispuskuroinnilla jopa jonkinlaista nopeusetua, kun sillä voidaan tehdä piirron jälkeen kuvapinnan "päivitys" vain tiettyyn alueeseen ruudusta. Tässä yksinkertainen esimerkki joka selostaa asian:
SDL_Surface *kuva; kuva = SDL_LoadBMP("kuva.bmp"); int x = 1, y = 5; SDL_Rect alue; // alue mihin kuva piirretään näytöllä alue.x = x; alue.y = y; if (SDL_BlitSurface( kuva, NULL, naytto, &alue ) < 0) { // huomaa sama piirtofunktio kuin käytettäessä kaksoispuskurointia printf("Virhe %s\n", SDL_GetError()); } SDL_FreeSurface(kuva); // sitten tärkein ero kaksoispuskurointiin: näytön päivitys; funktio päivittää muuttuneen alueen. SDL_UpdateRect( naytto, x, y, kuva->w, kuva->h ); SDL_Delay(5000); // odotellaan
Käyttämämme SDL_UpdateRect
päivittää muuttuneen alueen. Parametrina annetaan päivitettävä kuvapinta, päivitysalueen vasemman yläkulman koordinaatit sekä alueen leveys ja korkeus.
Tarkempaa piirtämistä
Nyt käyttämämme funktiot piirtävät aina koko bittikartan. Aina tähän ei ole syytä, esimerkiksi animoinnissa on kätevää näyttää vain tietty osa kuvasta. Saatat jo keksiä miten tämä toteutetaan. Meidän tulee vain hieman muokata SDL_BlitSurface
:lle parametrina antamaamme SDL_Rect
-aluetta. Tässä sitten piirtofunktio, joka piirtää ruudulle tietyn alueen kuvasta:
void PiirraKuva(SDL_Surface *kuva, SDL_Surface *naytto, int kuvax, int kuvay, int leveys, int korkeus, int nayttox, int nayttoy) { SDL_Rect kuvaalue; // alue, mikä kuvasta piirretään kuvaalue.x = kuvax; kuvaalue.y = kuvay; kuvaalue.h = korkeus; kuvaalue.w = leveys; SDL_Rect nayttoalue; // alue näytöllä, jolle kuva piirretään nayttoalue.x = nayttox; nayttoalue.y = nayttoy; SDL_BlitSurface(kuva, &kuvaalue, naytto, &nayttoalue); }
Jo aiemminkin käyttämämme SDL_BlitSurface
ottaa siis parametrina lähdekuvapinnan osoittimen, osoittimen aluetietueeseen jonka alueelta piirretään, kohdekuvapinnan osoittimen ja osoittimen alueeseen, jolle kuva piirretään. Kotiläksynä voitkin kokeilla kutsua tätä funktiota erilaisilla arvoilla ja katsella mitä tapahtuu. Voit myös kokeilla, mitä tapahtuu jos määrittelet nayttoalue:elle h:n ja w:n.
Jos joku ei vielä hoksannut, niin SDL_Rect
-tietotyypin alueella on neljä kokonaislukuarvoa: x-koordinaatti (x), y-koordinaatti (y), korkeus (h) ja leveys (w).
Äskeinen bittikartan piirto jättää meille erään ongelman: piirrämme jokaisen pikselin bittikartan tietyltä alueelta. Jos esimerkiksi piirrät pelaajahahmon, sen ympärille jää taustaväriä, joka pitää saada pois näkyvistä. Tämä onnistuu asettamalla tietty väri läpinäkyväksi:
SDL_SetColorKey(kuva, SDL_SRCCOLORKEY, SDL_MapRGB(kuva->format, 0,0,0));
Funktiolle annetaan parametrina osoitin pintaan, josta tietty väri asetetaan läpinäkyväksi. Tämän jälkeen annetaan ohjausliput. Viimeinen parametri kertoo, mikä väri asetetaan läpinäkyväksi. Tässä tapauksessa se on RGB:nä 0,0,0 eli musta.
Viimeisin muokkaus 3.5.2006
Hieno ja perusteellinen opas, mutta löysin yhden typon:
P*ska kijasto?
Korjattu.
Kun tuossa on läpinäkyvyydestä, en ymmärrä mihin kohtaan ohjelmassa tuo SDL_SetColorKey tulisi laittaa?
Kokeiltu on jo kohtuullisen paljon ja hermot menee kun mikään ei taas(kaan) suostu toimimaan :)
Ennen SDL_BlitSurface()
-funktiota.
Minäkin löysin typon, peräti koodista:
kuvaalue.x = kvuax;
Korjattu.
Mistä tuon SDL_gfx.dll saa ku sitä ei ollu paketissa? Eikä myöskään siinä omatekemässä, tai siis Heikinhän se on...
En ymmärrä miten tuota kuvaa saa näkymään.
Jos käytät kaksoispuskurointia, muistathan käyttää SDL_Flip(naytto)-funktiota?
Aivan yskinkertaista.
Onko värisyvyys varmasti tarkoitus määritellä 30-bittiseksi noilla parilla rivillä? Ettei kuitenkin 32-bittiseksi, kuten ensimmäisessä tapauksessa? Hyvä opas joka tapauksessa. Sain kaiken toimimaan juuri niin kuin tuossa neuvotaan.
Tosiaan 32-bittinenhän sen pitäisi olla. Typoja tullut :(
___ yskinkertainen esimerkki____
Korjattu, kylläpäs sinä nyt etsit niitä typoja.
Mitä kaikkia tän tapasia tarvitaan?
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "SDL.h"
ku nois se heittää niin miljoona errorii
Alussa on C:n standardikirjastoja, sitten string.h on C++:n standardi-merkkijonokirjasto ja SDL.h sisältää SDL:n funktioiden esittelyt.
Esimerkiksi stdio.h:ta tarvitaan muistaakseni esim. printf()-funktioon (en ole C-koodari). Jotta voisit käyttää SDL:ää, täytyy tuo SDL.h sisällyttää, tosin useammin toimii #include <SDL.h> tai #include <SDL/SDL.h>
SDL_BlitSurface(kuva, &kuvaalue, naytto, &nayttoalue);
SDL_BlitSurface ottaa siis parametrina lähdekuvapinnan osoittimen, viittauksen aluetietueeseen jonka alueelta piirretään, kohdekuvapinnan osoittimen ja viittauksen alueeseen, jolle kuva piirretään
eikös sille kuitenkin anneta viittauksien sijasta osoittimet..
SDL_Rect kuvaalue; // alue, mikä kuvasta piirretään SDL_Rect * osoitin = &kuvaalue;
voiko gif kuvia ladata näytölle
Voi, jos käytät lisäkirjastoa SDL_image
Juhan: Noinhan se tietysti menee, korjattu :)
Yhden typoillun kohdan löysin minäkin, "Meidän tulee vain hieman mukata". Ilmeisesti muokkauksesta kuitenkin kyse :)
Mainio opas kuitenkin kokonaisuudessaan.
Korjattu nyt tämäkin typo. Seuraavan kerran kun kirjoitan oppaan, lupaan käyttää oikolukua...
heikki kirjoitti:
Korjattu, kylläpäs sinä nyt etsit niitä typoja.
Eikö se kerro siitä, että lueskelen paljon opasta :) Kyllä niitä aina putkahtaa. Kun opasta lukee paljon niin ne tarttuu silmiin ;))
Yritin tehdä jutun joka piirtää kuvan ohjelmaan mutta se ei toimi. Mikä on vikana?
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "SDL.h" #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 SDL_Surface *screen = NULL; void piirrakuva(SDL_Surface *ukko, SDL_Surface *naytto, int x, int y) { SDL_Rect alue; alue.x = x; alue.y = y; SDL_BlitSurface(ukko, NULL, naytto, &alue); } int main(int argc, char *argv[]) { if( SDL_Init(SDL_INIT_VIDEO) < 0 ) { fprintf(stderr, "SDL:n alustus ei onnistunut: %s\n", SDL_GetError()); return 0; } SDL_Surface * naytto; naytto = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 16, SDL_HWSURFACE); SDL_Surface * ukko; ukko=SDL_LoadBMP("kuva1.bmp"); piirrakuva(ukko, naytto, 100, 100); SDL_Delay(5000); SDL_Quit(); return 0; }
Auttaisi huomattavasti jos kertoisit miten ei toimi, esim. meneekö kääntäjästä läpi tms.
No, nyt kun tuota katsoin niin missään et päivitä näyttöä. Et näemmä käytä kaksoispuskurointia, joten sinun on käytettävä SDL_UpdateRect()-funktiota (esitelty tässä osassa). Tai sitten voit ottaa käyttöön kaksoispuskuroinnin (SDL_DOUBLEBUF ikkunan asetuksiin) ja piirron jälkeen kutsut funktiota SDL_Flip(naytto)
Vikana oli että kuva ei tullut näkyviin, mutta nyt sain sen toimimaan laittamalla SDL_DOUBLEBUF ja SDL_Flip(naytto).
Hyvä opas. Mulla oli vain vaikeuksia alussa ymmärtää oikea kohta tuolle SDL_Flip-funktiolle :)
Toimiiko tuo PiirräKuva funktio vaikka pinta: kuva korvattaisiin jollain muulla nimellä?
Jep,erittäin yksinkertaista.
mul ei ainakaan toimi, vaik laitan ton flipin
Mikähän mahtaa olla vialla kun ohjelma kaatuu heti käynnistyksen yhteydessä?
Tässä koko koodi:
#include <SDL/SDL.h> int main(int argc, char *argv[]) { void PiirraKuva(SDL_Surface *kuva, SDL_Surface *naytto, int x, int y); SDL_Surface *naytto; SDL_Surface *kuva; naytto = SDL_SetVideoMode(1024, 768, 30, SDL_HWSURFACE|SDL_DOUBLEBUF); kuva = SDL_LoadBMP("kuva.bmp"); PiirraKuva(kuva, naytto, 50, 50); SDL_Flip(naytto); SDL_Delay(5000); return 0; } void PiirraKuva(SDL_Surface *kuva, SDL_Surface *naytto, int x, int y) { SDL_Rect alue; alue.x = x; alue.y = y; SDL_BlitSurface(kuva, NULL, naytto, &alue); }
EDIT: Ongelma selvisi. En ollut alustanut SDL:ää.
Typo report:
SDL_Rect alue; // mille alueellä näyttöä kuva piirretään
Mulla Windows XP havaitsee seuraavassa ohjelmassa virheen, ja sulkee ohjelman.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <SDL/SDL.h> #include <windows.h> SDL_Surface *screen = NULL; //kuvan piirtäminen void PiirraKuva(SDL_Surface *kuva, SDL_Surface *naytto, int x, int y) { SDL_Rect alue; alue.x = x; alue.y = y; SDL_BlitSurface(kuva, NULL, naytto, &alue); SDL_Flip(naytto); } //pääohjelma int main (int argc, char *argv[]) { SDL_Surface *naytto; SDL_Surface *kuva; naytto = SDL_SetVideoMode(200, 200, 30, SDL_HWSURFACE|SDL_DOUBLEBUF); kuva=SDL_LoadBMP("kuvatiedosto.bmp"); // ruudulle: PiirraKuva(kuva, naytto, 10, 10); return 0; }
huomasin tämmösen kivan pikku bugin, eli jos sulla on 7 (en tiiä vistasta) ni pelkkä SLD_LoadBMP("kuvatiedosto.bmp") ei riitä, tulee null surface error. Vähän aikaa kun googlasin löysin ongelmaan ratkasun -> eli sun pitää kirjottaa SLD_LoadBMP("/kuvatiedosto.bmp") että kuvaa tulee esille. Tämmönen pieni bugi/ w/e on päässy ilmestymään
Tuo TheEskon ohjelma toimii, kun poistaa #include <windows.h> ja laittaa lisää aikaa eli laitoin SDL_Delay(5000);
Toimii ainakin linuxissa.
Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.