EDIT: sainkin jo korjattua. Muokkasin teksti ja arvattuTeksti -funktiot sellaisiksi, että ne ottaa kuva-surfacen parametrinä ja sitten funktion sisällä tyhjennetään surface SDL_FreeSurfacella:
surface teksti(surface kuva, string teksti, font fontti) { freesurface(kuva); return txt(teksti, fontti); } int main() { kuva = teksti(kuva, "testi", fontti); }
Saa silti toki arvostella koodia.
Tein tässä tuollaisen pikkupelin ja huomasin, että tässä on ihan massiiviset muistivuodot. Koodin toimintaperiaate on, että käytän vain yhtä surfacea, piirrän sen ja sama uudestaan. Tässä pieni esimerkki:
SDL_Surface *kuva; while(loop) { kuva = teksti("moi"); piirraKuva(kuva); kuva = teksti("terve"); piirraKuva(kuva); }
Tekstin luon SDL_TTF:llä. Ongelma on ilmeisesti se, että aina kun käytän teksti()-funktiota, niin edellisen pointterin osoittamaa muuttujaa ei koskaan vapauteta ja se jää syömään muistia. Miten voisin siis tyhjentää tuon vanhan muuttujan järkevästi ennen uuden tekemistä. Olen yrittänyt FreeSurfacella säätämistä, mutta olen tähän asti onnistunut vain kaatamaan pelin korjausyrityksilläni.
Tässä vielä koko koodi:
#include <SDL/SDL.h> //#include <SDL/SDL_image.h> #include <SDL/SDL_ttf.h> #include <string> #include <vector> #include <fstream> using namespace std; #pragma comment(lib, "SDLmain.lib") #pragma comment(lib, "SDL.lib") //#pragma comment(lib, "SDL_image.lib") #pragma comment(lib, "SDL_ttf.lib") struct sanat_struct { string sana; int alku; int kesto; int paikka; }; struct rgb { int r; int g; int b; }; void piirraKuva(SDL_Surface *kuva, SDL_Surface *naytto, int x, int y); void piirraKuva(SDL_Surface *kuva, SDL_Surface *naytto, int kuvax, int kuvay, int leveys, int korkeus, int nayttox, int nayttoy); SDL_Surface *arvattuTeksti(string arvaus, string koko_teksti, TTF_Font *font); SDL_Surface *teksti(string teksti, TTF_Font *fontti); void tyhjenna(SDL_Surface *pinta); void tyhjenna(SDL_Surface *pinta, int x, int y, int w, int h); void tyhjenna(SDL_Surface *pinta, int x, int y, int w, int h, rgb vari); string intToString(int luku); string floatToString(float luku); int stringToInt(string luku); sanat_struct explode(string separator, string value); int random(int min, int max); vector<sanat_struct> lataaSanat(string tiedostonimi); int main(int argc, char *argv[]) { // Alustetaan SDL if(SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "SDL:n alustus ei onnistunut: %s\n", SDL_GetError()); return 0; } // Alustetaan SDL_TTF ja ladataan fontti if(TTF_Init() < 0) { fprintf(stderr, "SDL_TTF:n alustus ei onnistunut: %s\n", TTF_GetError()); return 0; } TTF_Font *fontti = TTF_OpenFont("AlteHaasGroteskBold.ttf", 32); TTF_Font *keskifontti = TTF_OpenFont("AlteHaasGroteskBold.ttf", 24); TTF_Font *pikkufontti = TTF_OpenFont("AlteHaasGroteskBold.ttf", 12); if(!fontti) { fprintf(stderr, "Fontin lataus ei onnistunut: %s\n", TTF_GetError()); return 0; } // Alustetaan SDL_Image /*int img_flags = IMG_INIT_PNG; int img_initted = IMG_Init(img_flags); if(img_initted&img_flags != img_flags) { fprintf(stderr, "SDL_Imagen alustus ei onnistunut: %s\n", IMG_GetError()); return 0; } SDL_Surface *tausta; tausta = IMG_Load("tausta.png"); if(!tausta) { fprintf(stderr, "Taustakuvan lataus ei onnistunut: %s\n", IMG_GetError()); return 0; }*/ SDL_Surface *tausta; tausta = SDL_LoadBMP("tausta.bmp"); if(!tausta) { fprintf(stderr, "Taustakuvan lataus ei onnistunut: %s\n", SDL_GetError()); return 0; } // Muuttujat ja muut setit srand(SDL_GetTicks()); SDL_EnableUNICODE(true); SDL_Surface *naytto = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE|SDL_RESIZABLE|SDL_DOUBLEBUF); SDL_Surface *kuva = 0; SDL_WM_SetCaption("Sanapeli HC 0.1b", "Sanapeli HC 0.1b"); unsigned int frame = 0; bool kaynnissa = true; Uint32 aika = SDL_GetTicks(); Uint32 aloitusAika, loppuAika; Uint32 aikaErotus = -1; SDL_Event tapahtuma; int fps; SDL_Color valkonen = {255, 255, 255}; string arvaus = ""; char ch; bool peliLoppu = false; bool valikkoMode = true; vector<sanat_struct> sanat; // Ladataan sanat tiedostosta sanat = lataaSanat("sanat.txt"); // Mainloop while(kaynnissa) { // Tapahtumat while(SDL_PollEvent(&tapahtuma)) { if(tapahtuma.type == SDL_QUIT) kaynnissa = false; if(tapahtuma.type == SDL_KEYDOWN) { if(tapahtuma.key.keysym.sym == SDLK_ESCAPE) kaynnissa = false; if(tapahtuma.key.keysym.sym == SDLK_RETURN) { if(valikkoMode) valikkoMode = false; else if(peliLoppu) { peliLoppu = false; aikaErotus = -1; sanat = lataaSanat("sanat.txt"); arvaus = ""; } } else if(tapahtuma.key.keysym.sym == SDLK_BACKSPACE) { arvaus = arvaus.substr(0, arvaus.length()-1); } else { if(tapahtuma.key.keysym.sym >= 65 && tapahtuma.key.keysym.sym <= 90 || tapahtuma.key.keysym.sym >= 97 && tapahtuma.key.keysym.sym <= 122 || tapahtuma.key.keysym.sym == 132 || tapahtuma.key.keysym.sym == 142 || tapahtuma.key.keysym.sym == 148 || tapahtuma.key.keysym.sym == 153) { if ((tapahtuma.key.keysym.unicode & 0xFF80) == 0) { ch = tapahtuma.key.keysym.unicode & 0x7F; } else { kaynnissa = false; } arvaus += ch; } } } if(!kaynnissa) break; } // Tyhjennetään näyttö ja piirretään taustakuva. piirraKuva(tausta, naytto, 0, 0); //FPS /*if(frame % 500 == 0) { fps = frame / ((SDL_GetTicks() - aika) / 1000.0); frame = 0; aika = SDL_GetTicks(); } kuva = teksti("FPS: " + intToString(fps), fontti); piirraKuva(kuva, naytto, 0, 0);*/ //Piirtäminen ja toiminta alkaa if(peliLoppu) { kuva = teksti("Hävisit pelin! Kestit "+floatToString(loppuAika / 1000.0f)+" sekuntia.", fontti); piirraKuva(kuva, naytto, 224, 310); kuva = teksti("ENTER = Uusi peli", fontti); piirraKuva(kuva, naytto, 350, 350); } else if(valikkoMode) { kuva = teksti("Aloita painamalla ENTER.", fontti); piirraKuva(kuva, naytto, 324, 310); } else { if(aikaErotus == -1) aikaErotus = SDL_GetTicks(); aloitusAika = SDL_GetTicks() - aikaErotus; kuva = teksti(arvaus, pikkufontti); piirraKuva(kuva, naytto, 206, 561); kuva = teksti("Aika: "+floatToString(aloitusAika / 1000.0f), keskifontti); piirraKuva(kuva, naytto, 10, 10); for(int i=0;i<sanat.size();i++) { if((sanat[i].alku + sanat[i].kesto) <= aloitusAika) { // Peli loppuu peliLoppu = true; loppuAika = aloitusAika; continue; } else if(sanat[i].sana == arvaus) { // Sana arvataan ja poistetaan arvaus = ""; sanat.erase(sanat.begin()+i); } else if(sanat[i].alku <= aloitusAika) { // Piirretään sanat kuva = arvattuTeksti(arvaus, sanat[i].sana, fontti); if(sanat[i].paikka == -1) sanat[i].paikka = random(0, 570 - kuva->w); piirraKuva(kuva, naytto, 210+sanat[i].paikka, 116+(((float)aloitusAika-sanat[i].alku) / (float)sanat[i].kesto * 420)); } } } SDL_Flip(naytto); frame++; } TTF_CloseFont(fontti); TTF_CloseFont(keskifontti); TTF_CloseFont(pikkufontti); SDL_FreeSurface(kuva); SDL_FreeSurface(tausta); TTF_Quit(); //IMG_Quit(); SDL_Quit(); 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); if(kuva) SDL_FreeSurface(kuva); } void piirraKuva(SDL_Surface *kuva, SDL_Surface *naytto, int kuvax, int kuvay, int leveys, int korkeus, int nayttox, int nayttoy) { SDL_Rect kuvaalue; kuvaalue.x = kuvax; kuvaalue.y = kuvay; kuvaalue.h = korkeus; kuvaalue.w = leveys; SDL_Rect nayttoalue; nayttoalue.x = nayttox; nayttoalue.y = nayttoy; SDL_BlitSurface(kuva, &kuvaalue, naytto, &nayttoalue); } SDL_Surface *arvattuTeksti(string arvaus, string koko_teksti, TTF_Font *fontti) { // Piirretään valkoinen teksti SDL_Color valkonen = {255, 255, 255}; SDL_Surface *koko = TTF_RenderText_Blended(fontti, koko_teksti.c_str(), valkonen); // Piirretään mahdollinen sininen arvaus-osuus if(arvaus.length() > 0 && arvaus == koko_teksti.substr(0, arvaus.length())) { SDL_Color sininen = {80, 86, 145}; SDL_Surface *arv = TTF_RenderText_Blended(fontti, arvaus.c_str(), sininen); rgb vari = {67,67,67}; tyhjenna(koko, 0, 0, arv->w, arv->h, vari); piirraKuva(arv, koko, 0, 0); SDL_FreeSurface(arv); } return koko; } SDL_Surface *teksti(string teksti, TTF_Font *fontti) { SDL_Color valkonen = {255, 255, 255}; SDL_Surface *pinta = TTF_RenderText_Blended(fontti, teksti.c_str(), valkonen); return pinta; } void tyhjenna(SDL_Surface *pinta) { Uint32 musta = SDL_MapRGB(pinta->format, 0, 0, 0); SDL_Rect alue = {0, 0, pinta->w, pinta->h}; SDL_FillRect(pinta, &alue, musta); } void tyhjenna(SDL_Surface *pinta, int x, int y, int w, int h) { Uint32 musta = SDL_MapRGB(pinta->format, 0, 0, 0); SDL_Rect alue = {x, y, w, h}; SDL_FillRect(pinta, &alue, musta); } void tyhjenna(SDL_Surface *pinta, int x, int y, int w, int h, rgb vari) { Uint32 tausta = SDL_MapRGB(pinta->format, vari.r, vari.g, vari.b); SDL_Rect alue = {x, y, w, h}; SDL_FillRect(pinta, &alue, tausta); } string intToString(int luku) { char buffer[255]; sprintf(buffer, "%d", luku); return buffer; } string floatToString(float luku) { char buffer[255]; sprintf(buffer, "%.3f", luku); return buffer; } int stringToInt(string luku) { int palautus; palautus = atoi(luku.c_str()); return palautus; } sanat_struct explode(string separator, string value) { vector<string> taulukko; string temp = ""; for(int x=0;x<value.length();x++) { if(value.substr(x, 1) == separator) { taulukko.push_back(temp); temp = ""; continue; } temp += value.substr(x, 1); } taulukko.push_back(temp); sanat_struct palautus; palautus.sana = taulukko[0]; palautus.alku = stringToInt(taulukko[1]); palautus.kesto = stringToInt(taulukko[2]); palautus.paikka = -1; return palautus; } int random(int min, int max) { return rand() % (max-min) + min; } vector<sanat_struct> lataaSanat(string tiedostonimi) { vector<sanat_struct> sanat; ifstream tiedosto(tiedostonimi.c_str()); if(tiedosto.is_open()) { string rivi; while(!tiedosto.eof()){ getline(tiedosto, rivi); sanat.push_back(explode("|", rivi)); } } tiedosto.close(); return sanat; }
punppis kirjoitti:
Miten voisin siis tyhjentää tuon vanhan muuttujan järkevästi ennen uuden tekemistä.
Tietenkin lisäämällä piirtämisen jälkeen vapautuksen.
kuva = teksti(); piirra(kuva); SDL_FreeSurface(kuva);
Tuolla tavalla ei toiminut, koska teksti-funktion sisällä tehdään uusi surface. Ehkä tein jotain väärin... No enivei nyt toimii.
Kyllä se juuri tällä tavalla toimii. Tietenkin pinta pitää vapauttaa vasta ihan viimeisen piirron jälkeen; jos tämän hahmottaminen tuntuu aivan ylivoimaiselta, voi yksinkertaisesti vapauttaa sen juuri ennen uuden pinnan varaamista (kuten itse ongelman ratkaisit). Muista vielä ohjelman lopussa vapauttaa viimeinenkin pinta.
Aihe on jo aika vanha, joten et voi enää vastata siihen.