The leak is in the drawTile
function. You create a SDL_Texture
but never destroy it. And you do that for every tile.
You only have to create a texture once. The graphics driver will usually store it in video memory and you can reference it as many times as you want. I recommend you put the image loading and texture creating part into an initialization function at the beginning of your program or whenever it is appropriate to load level data.
There are a few other issues in your code:
- The way you are creating the
SDL_Rect
data is problematic. Their lifetime will end immediately after assignment to the pointer and you now have undefined behavior. Even if you create the struct as a local variable, there’s no need to call SDL_free
on it. I’m guessing it is placed on the stack and that’s why you need the char* source[1]
;
- You only need to call
SDL_RenderPresent
after you’ve drawn all tiles to the screen. If you do it after every tile, only the last tile will show up in double buffered configurations (which is usually the default).
Here’s a very simple example on how to fix these issues:
texmanage.cpp
#include <SDL.h>
#include <SDL_image.h>
#define TILE_SIZE 32
static SDL_Texture* tileTexture;
static int tilemap[32][32];
static SDL_Renderer * mainRenderer;
bool loadIMG(const char* imgPath, SDL_Texture** dest)
{
SDL_Surface* surf = IMG_Load(imgPath);
*dest = SDL_CreateTextureFromSurface(mainRenderer, surf);
if (!*dest)
{
printf("Unable to load image/create texture for %s! SDL_Error: %s\n", imgPath, SDL_GetError());
return false;
}
SDL_FreeSurface(surf);
return true;
}
void initTilemap()
{
loadIMG("SOUVUTU0.png", &tileTexture);
/* Just some random test data */
for (int y = 0; y < 32; y++) {
for (int x = 0; x < 32; x++) {
tilemap[y][x] = ((x-7)*(y-7))%8 + (y*x*13)%8*8;
}
}
}
void drawTile(int id, int tileX, int tileY, int offsetX, int offsetY, int width)
{
const SDL_Rect tileSelect = {.x = (id / 8) * width, .y = (id % 8) * width, .w = width, .h = width};
const SDL_Rect tileClip = {.x = tileX * width + offsetX, .y = tileY * width + offsetY, .w = width, .h = width};
SDL_RenderCopy(mainRenderer, tileTexture, &tileSelect, &tileClip);
}
void drawTilemap(int startX, int startY, int endX, int endY, int offsetX, int offsetY)
{
SDL_RenderClear(mainRenderer);
for(int dy = startY; dy < endY; dy++)
{
for(int dx = startX; dx < endX; dx++)
{
drawTile(tilemap[dy][dx], dx, dy, offsetX, offsetY, TILE_SIZE);
}
}
SDL_RenderPresent(mainRenderer);
}
int main(int argc, char * argv[])
{
SDL_Init(SDL_INIT_VIDEO);
SDL_Window * window = SDL_CreateWindow("Loading...", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 400, 400, 0);
mainRenderer = SDL_CreateRenderer(window, -1, 0);
initTilemap();
int done = 0;
while (!done) {
SDL_Event e;
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT) {
done = 1;
} else if (e.type == SDL_KEYUP) {
Uint32 sym = e.key.keysym.sym;
if (sym == SDLK_ESCAPE) {
done = 1;
}
}
}
int ww, wh;
int mx, my;
SDL_GetWindowSize(window, &ww, &wh);
SDL_GetMouseState(&mx, &my);
drawTilemap(0, 0, 31, 31, mx - ww, my - wh);
}
SDL_DestroyTexture(tileTexture);
SDL_DestroyRenderer(mainRenderer);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Note that this is a minimal example to get your code into a working state with no error checking. I have not tested it extensively. I also added some offset arguments to make to tilemap move around with the mouse. The code is C++11, I think. I forgot when they started allowing assigning struct member by name on initialization.
If you have questions about it, don’t hesitate to ask.