TTF_RenderUTF8_Solid Runtime error
huabanlu opened this issue · 1 comments
I wanted to design a reusable Font class (Fontu), but it didn't work as expected on windows. An error occurred at SDL_Surface *textSurface = TTF_RenderUTF8_Solid(All::getInstance().getFont("test")->getTTF(), textureText.c_str(), textColor);
In Fontu::loadFromFile, if mFont = TTF_OpenFont(path.c_str(), 32); is used, it will run normally, if it is read from the file, it will run incorrectly.I expect to read from the file.
The following code is modified from "https://lazyfoo.net/tutorials/SDL/16_true_type_fonts/index.php"
/*This source code copyrighted by Lazy Foo' Productions 2004-2023
and may not be redistributed without written permission.*/
// Using SDL, SDL_image, SDL_ttf, standard IO, math, and strings
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <stdio.h>
#include <string>
#include <cmath>
#include <iostream>
#include <unordered_map>
#include <vector>
#include <fstream>
#include <iostream>
// Screen dimension constants
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
class Fontu
{
public:
Fontu() {}
~Fontu() {}
void free() {}
bool loadFromFile(std::string path)
{
std::vector<unsigned char> data;
FILE *fp = fopen(path.c_str(), "rb");
fseek(fp, 0, SEEK_END);
size_t size = ftell(fp);
data.resize(size);
fseek(fp, 0, SEEK_SET);
fread(data.data(), size, 1, fp);
fclose(fp);
TTF_Font *tmp = nullptr;
SDL_RWops *rw = SDL_RWFromMem(data.data(), data.size());
tmp = TTF_OpenFontRW(rw, 1, 32);
setTTF(tmp);
// mFont = TTF_OpenFont(path.c_str(), 32);
printf("loadFromFile mFont: %p\n", tmp);
return true;
}
void setTTF(TTF_Font *ttf)
{
mFont = ttf;
}
TTF_Font *getTTF()
{
return mFont;
}
private:
TTF_Font *mFont;
};
class All
{
public:
static All &getInstance()
{
static All instance;
return instance;
}
void setFont(std::string name, Fontu *font)
{
mFontMap[name] = font;
}
Fontu *getFont(std::string name)
{
return mFontMap[name];
}
private:
All() {}
~All() {}
All(const All &) = delete;
All &operator=(const All &) = delete;
std::unordered_map<std::string, Fontu *> mFontMap;
};
// Texture wrapper class
class LTexture
{
public:
// Initializes variables
LTexture();
// Deallocates memory
~LTexture();
// Loads image at specified path
bool loadFromFile(std::string path);
// Creates image from font string
bool loadFromRenderedText(std::string textureText, SDL_Color textColor);
// Deallocates texture
void free();
// Set color modulation
void setColor(Uint8 red, Uint8 green, Uint8 blue);
// Set blending
void setBlendMode(SDL_BlendMode blending);
// Set alpha modulation
void setAlpha(Uint8 alpha);
// Renders texture at given point
void render(int x, int y, SDL_Rect *clip = NULL, double angle = 0.0, SDL_Point *center = NULL, SDL_RendererFlip flip = SDL_FLIP_NONE);
// Gets image dimensions
int getWidth();
int getHeight();
private:
// The actual hardware texture
SDL_Texture *mTexture;
// Image dimensions
int mWidth;
int mHeight;
};
// Starts up SDL and creates window
bool init();
// Loads media
bool loadMedia();
// Frees media and shuts down SDL
void close();
// The window we'll be rendering to
SDL_Window *gWindow = NULL;
// The window renderer
SDL_Renderer *gRenderer = NULL;
// Globally used font
// TTF_Font *gFont = NULL;
// Rendered texture
LTexture gTextTexture;
LTexture::LTexture()
{
// Initialize
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
LTexture::~LTexture()
{
// Deallocate
free();
}
bool LTexture::loadFromFile(std::string path)
{
// Get rid of preexisting texture
free();
// The final texture
SDL_Texture *newTexture = NULL;
// Load image at specified path
SDL_Surface *loadedSurface = IMG_Load(path.c_str());
if (loadedSurface == NULL)
{
printf("Unable to load image %s! SDL_image Error: %s\n", path.c_str(), IMG_GetError());
}
else
{
// Color key image
SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0, 0xFF, 0xFF));
// Create texture from surface pixels
newTexture = SDL_CreateTextureFromSurface(gRenderer, loadedSurface);
if (newTexture == NULL)
{
printf("Unable to create texture from %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
}
else
{
// Get image dimensions
mWidth = loadedSurface->w;
mHeight = loadedSurface->h;
}
// Get rid of old loaded surface
SDL_FreeSurface(loadedSurface);
}
// Return success
mTexture = newTexture;
return mTexture != NULL;
}
bool LTexture::loadFromRenderedText(std::string textureText, SDL_Color textColor)
{
Fontu *font = All::getInstance().getFont("test");
if (font == nullptr)
{
printf("Font not found in All instance!\n");
return false;
}
// Get the TTF font
TTF_Font *ttfFont = font->getTTF();
if (ttfFont == nullptr)
{
printf("TTF Font not loaded!\n");
return false;
}
// Get rid of preexisting texture
free();
// Render text surface
SDL_Surface *textSurface = TTF_RenderUTF8_Solid(All::getInstance().getFont("test")->getTTF(), textureText.c_str(), textColor);
if (textSurface == NULL)
{
printf("Unable to render text surface! SDL_ttf Error: %s\n", TTF_GetError());
}
else
{
// Create texture from surface pixels
mTexture = SDL_CreateTextureFromSurface(gRenderer, textSurface);
if (mTexture == NULL)
{
printf("Unable to create texture from rendered text! SDL Error: %s\n", SDL_GetError());
}
else
{
// Get image dimensions
mWidth = textSurface->w;
mHeight = textSurface->h;
}
// Get rid of old surface
SDL_FreeSurface(textSurface);
}
// Return success
return mTexture != NULL;
}
void LTexture::free()
{
// Free texture if it exists
if (mTexture != NULL)
{
SDL_DestroyTexture(mTexture);
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
}
void LTexture::setColor(Uint8 red, Uint8 green, Uint8 blue)
{
// Modulate texture rgb
SDL_SetTextureColorMod(mTexture, red, green, blue);
}
void LTexture::setBlendMode(SDL_BlendMode blending)
{
// Set blending function
SDL_SetTextureBlendMode(mTexture, blending);
}
void LTexture::setAlpha(Uint8 alpha)
{
// Modulate texture alpha
SDL_SetTextureAlphaMod(mTexture, alpha);
}
void LTexture::render(int x, int y, SDL_Rect *clip, double angle, SDL_Point *center, SDL_RendererFlip flip)
{
// Set rendering space and render to screen
SDL_Rect renderQuad = {x, y, mWidth, mHeight};
// Set clip rendering dimensions
if (clip != NULL)
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
// Render to screen
SDL_RenderCopyEx(gRenderer, mTexture, clip, &renderQuad, angle, center, flip);
}
int LTexture::getWidth()
{
return mWidth;
}
int LTexture::getHeight()
{
return mHeight;
}
bool init()
{
// Initialization flag
bool success = true;
// Initialize SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
// Set texture filtering to linear
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"))
{
printf("Warning: Linear texture filtering not enabled!");
}
// Create window
gWindow = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (gWindow == NULL)
{
printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
// Create vsynced renderer for window
gRenderer = SDL_CreateRenderer(gWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (gRenderer == NULL)
{
printf("Renderer could not be created! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
// Initialize renderer color
SDL_SetRenderDrawColor(gRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
// Initialize PNG loading
int imgFlags = IMG_INIT_PNG;
if (!(IMG_Init(imgFlags) & imgFlags))
{
printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
success = false;
}
// Initialize SDL_ttf
if (TTF_Init() == -1)
{
printf("SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError());
success = false;
}
}
}
}
return success;
}
bool loadMedia()
{
// Loading success flag
bool success = true;
// Open the font
std::string font_path = "./font/test.ttf";
Fontu *font = new Fontu();
font->loadFromFile(font_path);
All::getInstance().setFont("test", font);
// gFont = TTF_OpenFont(font_path.c_str(), 32);
if (All::getInstance().getFont("test") == NULL)
{
printf("Failed to load lazy font! SDL_ttf Error: %s\n", TTF_GetError());
success = false;
}
else
{
// Render text
SDL_Color textColor = {255, 255, 255};
if (!gTextTexture.loadFromRenderedText("hello world!", textColor))
{
printf("Failed to render text texture!\n");
success = false;
}
}
return success;
}
void close()
{
// Free loaded images
gTextTexture.free();
// Free global font
// TTF_CloseFont(Font::getInstance().getTTF());
// Font::getInstance().setTTF(nullptr);
// Destroy window
SDL_DestroyRenderer(gRenderer);
SDL_DestroyWindow(gWindow);
gWindow = NULL;
gRenderer = NULL;
// Quit SDL subsystems
TTF_Quit();
IMG_Quit();
SDL_Quit();
}
int main(int argc, char *args[])
{
// Start up SDL and create window
if (!init())
{
printf("Failed to initialize!\n");
}
else
{
// Load media
if (!loadMedia())
{
printf("Failed to load media!\n");
}
else
{
// Main loop flag
bool quit = false;
// Event handler
SDL_Event e;
// While application is running
while (!quit)
{
// Handle events on queue
while (SDL_PollEvent(&e) != 0)
{
// User requests quit
if (e.type == SDL_QUIT)
{
quit = true;
}
}
// Clear screen
SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0);
SDL_RenderClear(gRenderer);
// Render current frame
gTextTexture.render((SCREEN_WIDTH - gTextTexture.getWidth()) / 2, (SCREEN_HEIGHT - gTextTexture.getHeight()) / 2);
// Update screen
SDL_RenderPresent(gRenderer);
}
}
}
// Free resources and close SDL
close();
return 0;
}
I want someone to help me figure out why and fix it.
Version:
- SDL2-2.28.0
- SDL2_ttf-2.20.2
Your issue is in Fontu::loadFromFile
.
https://wiki.libsdl.org/SDL2/SDL_RWFromMem#remarks
the pointer you provide must remain valid until you close the stream
The std::vector<unsigned char>
is destroyed when exiting Fontu::loadFromFile