0
\$\begingroup\$

As the title says I would like to know how to reload a TTF_SDL Text during runtime in C++.

What I want to do is to update all the values of the text each frame so I can, to example change the font size and it will instantly change it in-game as well without needing to restart the entire game.

How would I do this?


Note : I'm using textures to render the text, using the SDL_CreateTextureFromSurface() function.


This is the way I'm currently trying to update the TTF Text :

void Text::Update(SDL_Renderer* renderer)
{
    SDL_FreeSurface(m_surface);
    SDL_DestroyTexture(m_texture);

    m_surface = new SDL_Surface();

    m_surface = TTF_RenderText_Solid(m_font, m_text.c_str(), m_color_fg);
    m_texture = SDL_CreateTextureFromSurface(renderer, m_surface);
}

And in the header file the two variables look like this :

SDL_Surface* m_surface = nullptr;
SDL_Texture* m_texture = nullptr;

And I currently get an error at SDL_FreeSurface(m_surface); which looks like this :

Thread 1: EXC_BAD_ACCESS (code=EXC_l386_GPFLT)

However, the code can also be 1 with a address that seem to change every time I run my application

However I sometimes get another error on this line :

m_surface = TTF_RenderText_Solid(m_font, m_text.c_str(), m_color_fg);

Instead, which has the following error code :

Thread 1: EXC_BAD_ACCESS (code=1, adress=0x11)

But the address on the error codes are not the same every time I run it.

(On both lines of code)


**Full sprite class code :**

Sprite.hpp :

//
//  Text.hpp
//  SDL_Project
//
//  Created by Someone... on 2016-03-18.
//  Copyright © 2016 Someone... All rights reserved.
//

#ifndef Text_hpp
#define Text_hpp

#include <stdio.h>
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2_ttf/SDL_ttf.h>

class Text {

public:

    Text(std::string __m_text, int x, int y, SDL_Renderer* renderer);
    void Render(SDL_Renderer* renderer);
    void Update(SDL_Renderer* renderer);

    void SetText(std::string text, SDL_Renderer* renderer);

    void Destroy();

private:

    SDL_Rect* m_rect = nullptr;
    SDL_Surface* m_surface = nullptr;
    SDL_Texture* m_texture = nullptr;
    SDL_Color m_color_fg;
    TTF_Font* m_font;

    std::string m_text;

};

#endif /* Text_hpp */

Sprite.cpp :

//
//  Text.cpp
//  SDL_Project
//
//  Created by Still made by someone on 2016-03-18.
//  Copyright © 2016 :). All rights reserved.
//

#include "Text.hpp"

Text::Text(std::string __m_text, int x, int y, SDL_Renderer* renderer)
{
    m_text = __m_text;
    m_font = TTF_OpenFont("arial.ttf", 24);

    m_rect = new SDL_Rect();

    m_rect->x = x;
    m_rect->y = y;

    m_surface = TTF_RenderText_Solid(m_font, m_text.c_str(), m_color_fg);
    if (m_surface == nullptr)
    {
        std::cout << "Failed to create text surface : " << SDL_GetError() << std::endl;
    }

    m_texture = SDL_CreateTextureFromSurface(renderer, m_surface);
    if (m_texture == nullptr)
    {
        std::cout << "Failed to create text texture : " << SDL_GetError() << std::endl;
    }

    TTF_CloseFont(m_font);
    SDL_FreeSurface(m_surface);
}

void Text::Render(SDL_Renderer* renderer)
{
    SDL_RenderCopy(renderer, m_texture, NULL, m_rect);
}

void Text::Update(SDL_Renderer* renderer)
{
    // This part throws me a error and makes the game crash.

    std::cout << "1 : " << SDL_GetError() << std::endl;
    SDL_FreeSurface(m_surface);
    SDL_DestroyTexture(m_texture);

    std::cout << "2 : " << SDL_GetError() << std::endl;

    m_surface = TTF_RenderText_Solid(m_font, m_text.c_str(), m_color_fg);
    m_texture = SDL_CreateTextureFromSurface(renderer, m_surface);
}

void Text::SetText(std::string text, SDL_Renderer* renderer)
{
    m_text = text;
    Update(renderer);
}

void Text::Destroy()
{

}
\$\endgroup\$
  • \$\begingroup\$ You will always have to recreate the texture if you change the size... There's no auto update, you could create a class for it \$\endgroup\$ – Xriuk Mar 20 '16 at 9:50
  • \$\begingroup\$ @Xriuk I have a class for it, which has a "ApplyChanges" function, which "overwrites" the SDL_Surface and SDL_Texture however it has a memory leak :/ \$\endgroup\$ – BiiX Mar 20 '16 at 18:52
  • \$\begingroup\$ Are you destroying created surfaces after you create textures? And created textures when you overwrite them? \$\endgroup\$ – Xriuk Mar 20 '16 at 21:04
  • \$\begingroup\$ @Xriuk Yes, but they lead to EXC_BAD_ACCESS errors on 2 different places (it "switches" between those 2 different lines of code "randomly") The error lines are : SDL_FreeSurface(m_surface); and sometimes m_surface = TTF_RenderText_Solid(m_font, m_text.c_str(), m_color_fg); \$\endgroup\$ – BiiX Mar 21 '16 at 12:20
  • \$\begingroup\$ Could you post some code and some detailed errors? \$\endgroup\$ – Xriuk Mar 21 '16 at 13:23
2
\$\begingroup\$

You close your font with TTF_CloseFont at the end of your constructor. Remove that, and then the rendering should work. You want to move the closing code to the destructor.

Also, you release your m_surface first at the end of the Text-classes constructor, and then you do that again in the beginning of Update. You're essentially trying to delete already deleted memory, which obviously is something that might crash. You don't really even need the m_surface-member, it could just be replaced with a locally scoped SDL_Surface* variable.

\$\endgroup\$
  • \$\begingroup\$ So, I checked if the m_surface and m_texture were nullptrs, which they were. However now it has another bad_access error in TTF_RenderText_Solid instead. (in the update function) \$\endgroup\$ – BiiX Mar 22 '16 at 15:30
  • \$\begingroup\$ Please see my edit. \$\endgroup\$ – Tyyppi_77 Mar 22 '16 at 15:39
  • \$\begingroup\$ Yes, this solved my errors, however now I got a memory leak somewhere in the Update function. >.< I'm guessing I should start a new post about this problem? :3 \$\endgroup\$ – BiiX Mar 22 '16 at 15:47
  • 1
    \$\begingroup\$ Or just read trough your code, check all allocations and make sure that there is a matching safe deallocation for each one. It would be probably beneficial to replace the surface member with corresponding local variables that you can safely release. And accept this answer. \$\endgroup\$ – Tyyppi_77 Mar 22 '16 at 15:51
  • \$\begingroup\$ The memory leak is being "created" in the Update function in my text class. I don't understand what's creating it though o.o Am I doing something really dumb when I assign new values to my m_surface and m_texture maybe? :3 So confused... \$\endgroup\$ – BiiX Mar 23 '16 at 17:02
1
\$\begingroup\$

For a relatively small number of fonts, just pre-compile and include them with your game. For sprite-based text rendering, each combination of [Font][Size] is a different "font". You pass the font as a parameter to the text rendering methods so you can swap it easily.

Alternatively, you may be able to render each font using only the largest size (for highest resolution) and scale smaller fonts down during the render process (being mindful of aliasing).

As a side-note, be sure that you are legally allowed to redistribute the font. Some of the fonts included in your OS may not be public domain.

\$\endgroup\$
  • \$\begingroup\$ What if I want to change the text displays (the string of the text) during runtime? When I change the values of the text, it doesn't change in-game since it only uses the values I give it once during the initialization of the text. \$\endgroup\$ – BiiX Mar 20 '16 at 18:53
  • \$\begingroup\$ @BiiX, are you using this? libsdl.org/projects/SDL_ttf/docs/SDL_ttf.html \$\endgroup\$ – Jon Mar 20 '16 at 20:10
  • \$\begingroup\$ Yes, that's what I'm using. \$\endgroup\$ – BiiX Mar 21 '16 at 7:28
1
\$\begingroup\$

I have achieved this in my little testing and learning exercises. I have read that rendering text is a fairly expensive process, especially if using blended and wrapped mode. and so it should be avoided unless there is an actual change to the content.
My solution was to store the rendered texture inside whatever object has the text pinned to it and have a flag which is set if ether the text, font, textPosition or textColour are changed. This flag will trigger an updateText call on the next rendering loop which then recalculates the texts position to make sure it is still centered where it needs to be, and then regenerates the textTexture by first saving the surface locally then converting it to a texture.

forgive the missing stuff, lets assume this has an appropriate constructor and missing setters, getters and other button stuff is all there.

class MenuItem
{
public:
    void drawButton();

    void setFont(TTF_Font* newFont);
    //imagine several other setters and getters here...

private:
    bool _textUpdateNeeded;
    std::string text;
    SDL_Color textColour;
    SDL_Rect textPosition;
    TTF_Font* _font
    SDL_Renderer* _renderer;

    void updateTextTexture();
};

void MenuItem::setFont(TTF_Font* newFont)
{
    _font = newFont;
   //something has changed so we flag that the text needs updating on the next
   //render cycle
    _textUpdateNeeded = true; 
}
//Assume the other setters are much the same

void MenuItem::updateTextTexture()
{
    TTF_SizeText(_font, _text.c_str(), &_textPosition.w, &_textPosition.h);
    //some stuff here to match the centers of the text and the object its
    //being rendered against which goes beyond the scope of this question.

    SDL_Surface* temp = TTF_RenderText_Solid(_font, _text.c_str(), _textColour);
    _textTexture = SDL_CreatetextureFromSurface(_renderer, temp);
    _textUpdateNeeded = false;    
}

void MenuItem::drawButton()
{
    //Here we see if the text NEEDS to be updated first, If so then we will do
    //this now. If the text has not been changed we fall through this and just
    //keep rendering the old texture.
    if(_textUpdateNeeded) updateTextTexture();
    //imagine here we draw any bits that aren't the text first so the text
    //will appear on top of everything else.
    SDL_RenderCopy(_renderer, _textTexture, NULL, &_TextPosition);
}
\$\endgroup\$
  • 1
    \$\begingroup\$ In your updateTextTexture() you MUST destroy the temp surface with SDL_FreeSurface() or you'll experience memory leaks... \$\endgroup\$ – Xriuk May 19 '16 at 5:46
  • \$\begingroup\$ Noted, Thank you. Quite new to using SDL myself also so Learning slowly but surely. \$\endgroup\$ – Kal9001 May 20 '16 at 1:40
  • \$\begingroup\$ Almost forgot, same goes for the texture, destroy it with SDL_DestroyTexture() before replacing it. ;) \$\endgroup\$ – Xriuk May 20 '16 at 17:44

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.