1
\$\begingroup\$

I am creating 2D game with SDL2, and I have a problem with my label class. Every time I construct new label I have a memory leak, and I don't know what I do wrong! For example, I want to refresh every time the fps label on my game, I have a memory leak since the old texture seems that doesn't deletes. Here is the code of the Label class. Tell me please where I am wrong!

namespace se {

class WidgetLabel : public Widget {
public:
    WidgetLabel(std::shared_ptr<Display> t_display,const Vector2i& t_possition, const std::string& t_text, const int t_fontSize = 25, const Color& t_color = Color(255,255,255,255));
    WidgetLabel(const WidgetLabel& rhs) = delete;
    WidgetLabel& operator =(const WidgetLabel& rhs) = delete;
    ~WidgetLabel();
    void render();
    void update();
    void setBlendMode(SDL_BlendMode t_blending);
    void setAlphaMode(Uint8 t_alpha);

    void setPossition(const Vector2i& t_possitions);
    void setPossitionX(const int t_x);
    void setPossitionY(const int t_y);
    const Vector2i getPossition() const;
    void setFontPath(const std::string& t_path);
    void setText(const std::string& t_string);
    const Vector2i getResolution() const;
    int getWidth() const;
    int getHeight() const;

private:

    std::shared_ptr<Display>    m_display;
    TTF_Font                    *m_font;
    SDL_Surface                 *m_surface;
    SDL_Texture                 *m_texture;

    int                         m_width;
    int                         m_height;
    int                         m_possitionX;
    int                         m_possitionY;


    std::string                 m_text;
    int                         m_fontSize;
    std::string                 m_fontPath;
    Color                       m_color;

    void createSurface();

};

}

namespace se {

WidgetLabel::WidgetLabel(std::shared_ptr<Display> t_display,const Vector2i& t_possition, const std::string& t_text, const int t_fontSize, const Color& t_color) :
m_display(t_display),
m_surface(nullptr),
m_texture(nullptr),
m_width(0),
m_height(0),
m_possitionX(t_possition.getX()),
m_possitionY(t_possition.getY()),
m_text(t_text),
m_fontSize(t_fontSize),
m_fontPath("assets/fonts/serif.ttf"),
m_color(t_color)

{

    m_font = TTF_OpenFont(m_fontPath.c_str(), t_fontSize);

    if (m_font == nullptr) {
        std::cerr << "Faild to load SDL Font! Error: " << TTF_GetError() << '\n';
    }

    int imgflags = IMG_INIT_PNG;
    if ( !( IMG_Init(imgflags) & imgflags)) {
        std::cerr << "Couldn't not initialize sdl image" << '\n';
    }

    createSurface();

}

WidgetLabel::~WidgetLabel()
{
    SDL_DestroyTexture(m_texture);
    m_texture = nullptr;
}

void WidgetLabel::render()
{
    SDL_Rect rect_ = { m_possitionX, m_possitionY, m_width, m_height};
    SDL_RenderCopyEx(m_display->getRenderer(), m_texture, nullptr, &rect_, 0.0, nullptr, SDL_FLIP_NONE);
}
void WidgetLabel::setPossition(const Vector2i &t_possitions)
{
    m_possitionX = t_possitions.getX();
    m_possitionY = t_possitions.getY();
}

void WidgetLabel::setPossitionX(const int t_x)
{
    m_possitionX = t_x;
}
void WidgetLabel::setPossitionY(const int t_y)
{
    m_possitionY = t_y;
}
const Vector2i WidgetLabel::getPossition() const
{
    return Vector2i(m_possitionX, m_possitionY);
}

void WidgetLabel::setFontPath(const std::string &t_path)
{
    m_fontPath = t_path;
}
void WidgetLabel::setText(const std::string &t_string)
{
    m_text = t_string;
    createSurface();
}
void WidgetLabel::createSurface()
{

    m_surface = TTF_RenderText_Blended(m_font, m_text.c_str(), m_color);

    if ( m_surface == nullptr ) {
        std::cerr << "Unable to render text surface! Error: " << TTF_GetError() << '\n';
    } else {

        m_texture = SDL_CreateTextureFromSurface(m_display->getRenderer(), m_surface);

        if ( m_texture == nullptr ) {
            std::cerr << "Unable to create texture from renderer text! Error: " << SDL_GetError() << '\n';
        } else {

            m_width = m_surface->w;
            m_height = m_surface->h;

        }

        SDL_FreeSurface(m_surface);
        m_surface = nullptr;
    }

}
const Vector2i WidgetLabel::getResolution() const
{
    return Vector2i(m_width, m_height);
}

int WidgetLabel::getWidth() const
{
    return m_width;
}

int WidgetLabel::getHeight() const
{
    return m_height;
}
void WidgetLabel::update()
{

}

void WidgetLabel::setBlendMode(SDL_BlendMode t_blending)
{
    SDL_SetTextureBlendMode(m_texture, t_blending);
}

void WidgetLabel::setAlphaMode(Uint8 t_alpha)
{
    SDL_SetTextureAlphaMod(m_texture, t_alpha);
}

}

The render and update function called in main game loop. The shared pointer with t_display, is just the passed renderer .

\$\endgroup\$
1
\$\begingroup\$

Upon first glance, it looks like you call TTF_OpenFont to create your font in your label constructor, but you do not call TTF_CloseFont in the destructor. You should.

You also call createSurface in both the constructor and setText. When called from setText, createSurface will immediately allocate a new m_surface and m_texture, causing you to lose the things they were previously pointing at (and thus, leak them).

If you're going to use that model, you probably need createSurface to first check if the texture is non-null, and if so, release it prior to creating a new one. It's not clear that m_surface needs to be member at all since it appears you only create it for createSurface and always release it before the function ends. It may simplify your code if you can just make that surface a temporary local.

\$\endgroup\$
  • \$\begingroup\$ every time i use TTF_CloseFont i get this error from compiler: Process finished with exit code 139 (interrupted by signal 11: SIGSEGV) \$\endgroup\$ – pureofpure Jul 21 '16 at 15:59
  • \$\begingroup\$ Well, that is interesting but probably a different bug (maybe you are corrupting the font pointer or font data with a memory overwrite somewhere?). The fact is, however, that if you call TTF_OpenFont you should pair it with TTF_CloseFont to free the memory used by the font. \$\endgroup\$ – Josh Jul 21 '16 at 16:01
  • \$\begingroup\$ A quick search suggests that if your destructor is firing after TTF_Quit gets called, and you call TTF_Close in your destructor, you will crash (since TTF_Quit means you can no longer call most TTF_ functions). I'd step through in your debugger to see if that is the case. \$\endgroup\$ – Josh Jul 21 '16 at 16:03
  • \$\begingroup\$ Ok, seems the problem is caused by TTF_Quit(); since i remove TTF_Quit(); everything is works fine ! \$\endgroup\$ – pureofpure Jul 21 '16 at 16:03
  • \$\begingroup\$ You probably want TTF_Quit as well, just make sure all your widget labels get destroyed before you call it. \$\endgroup\$ – Josh Jul 21 '16 at 16:04
-1
\$\begingroup\$

I had the same problem but I have no memory leak now steady a steady line

so in my update drawing the renderer // SDL_Surface* surface is in my header and gets deleted on the ~destructor

if (surface == NULL) {
    surface = TTF_RenderText_Solid(getFont(), getText(), getColor());
    setMessage(SDL_CreateTextureFromSurface(getRender(), surface));
}

SDL_RenderCopy(getRender(), getMessage(), NULL, getMousePosition());

if (surface != NULL)
{
    SDL_BlitSurface(surface, NULL, surface, NULL);
    //SDL_FreeSurface(surface); // dont destroy

}
else
{
    // report error
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.