Question

I'm trying to write a basic program to render text in OpenGL using SDL_ttf. I have seen around a dozen questions answered on how to get the two to work together, almost all of which provide code that looks similar to what I'm using. I've seen no questions, however, that relate to the volatility I'm experiencing.

It's very strange. If I'm using a certain font, at a certain point size, with a certain output string, then the program works and outputs perfectly. In other cases, the program will run but the text surface comes out as garbage. And in other cases, the program crashes immediately.

For instance, if I call TTF_OpenFont( "font1.ttf" , 28 ), and then call TTF_RenderUTF8_Blended() with "Testing" as the output string, it crashes. But I tried a bunch of things and got very disturbing results.

-If I remove the "g" and try to output "Testin", then the program works and outputs as it's supposed to. O_o

-If I try to output "Test", I get garbage.

-If I try to output "Tst", the program works. o_O

-If I try to output "tst", the program crashes.

Furthermore, as I try out different fonts and point sizes I get garbage or crashes with different strings.

All error checking on SDL_ttf functions came back clean.

In other words, as far as I can tell, the only thing that's making the difference between whether the program works or not is what strings I'm passing to SDL_ttf functions. I have no idea in the world why that would be the case (the font directories are definitely accurate). All I can hypothesize is that I'm overlooking some kind of obvious memory leak or something.

With that said, here's my initialization code:

bool Init() {

if( SDL_Init(SDL_INIT_EVERYTHING) < 0 )
    return false;

//SDL_Surface* display_surface is declared outside of this function
if( (display_surface = SDL_SetVideoMode(640 , 480 , 32 , SDL_HWSURFACE | SDL_GL_DOUBLEBUFFER | SDL_OPENGL)) == NULL )
    return false;

SDL_GL_SetAttribute(SDL_GL_RED_SIZE,            8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,          8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,           8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE,          8);

SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,          16);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE,         32);

SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE,      8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE,    8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE,     8);
SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE,    8);

SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS,  1);

SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES,  2);

glClearColor(0, 0, 0, 0);
glClearDepth(1.0f);

glViewport(0, 0, 640, 480);

glMatrixMode( GL_PROJECTION );
glLoadIdentity();

glOrtho(0 ,640 ,480 ,0 ,1 ,-1);

glMatrixMode( GL_MODELVIEW );
glLoadIdentity();

//TTF_Font* test_font is declared outside of this function
test_font = TTF_OpenFont( "font1.ttf" , 28 ); 

SDL_Color text_color = { 255 , 255 , 255 };

//SDL_Surface* text_surface is declared outside of this function
text_surface = TTF_RenderUTF8_Blended( test_font, "Testing" , text_color );

//force powers of 2
int w_pow2 = 1;
int h_pow2 = 1;
while( w_pow2 < text_surface->w )
    w_pow2 *= 2;
while( h_pow2 < text_surface->h )
    h_pow2 *= 2;
text_surface->w = w_pow2;
text_surface->h = h_pow2;

GLuint color_format;

if ( text_surface->format->BytesPerPixel == 4 ) {
    if (text_surface->format->Rmask == 0x000000ff) 
        color_format = GL_RGBA;
    else 
        color_format = GL_BGRA;
    } 
else if ( text_surface->format->BytesPerPixel == 3 ) {
    if ( text_surface->format->Rmask == 0x000000ff) 
        color_format = GL_RGB;
    else 
        color_format = GL_BGR;
    } 

glGenTextures( 1 , &texture ); //GLuint texture is declared outside of this function
glBindTexture( GL_TEXTURE_2D , texture );
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR );
glTexImage2D( GL_TEXTURE_2D , 0 , text_surface->format->BytesPerPixel , text_surface->w , text_surface->h , 0 , color_format , GL_UNSIGNED_BYTE , text_surface->pixels ); 

return true;

}

my rendering code:

void Draw() {

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );

glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D , texture );

glBegin( GL_QUADS );
    glTexCoord2f( 0.0 , 0.0 );
    glVertex2f( 0.0 , 0.0 );

    glTexCoord2f( 1.0 , 0.0 );
    glVertex2f( text_surface->w , 0.0 );

    glTexCoord2f( 1.0 , 1.0 );
    glVertex2f( text_surface->w , text_surface->h );

    glTexCoord2f( 0.0 , 1.0 );
    glVertex2f( 0.0 , text_surface->h );
glEnd();

glDisable( GL_TEXTURE_2D );
glDisable( GL_BLEND );

SDL_GL_SwapBuffers();

}

and my cleanup:

void Cleanup() {

glDeleteTextures( 1 , &texture );

SDL_FreeSurface( text_surface ); 

TTF_CloseFont( test_font ); 

TTF_Quit();

SDL_Quit();

}

I will do my best to provide as many additional details as necessary.

Was it helpful?

Solution

TTF_RenderUTF8_Blended() generates an SDL_Surface (text_surface) of an arbitrary size based on how the TrueType renderer decides to plot the text. Later on, your code unilaterally re-sizes text_surface's width and height variables without actually changing the image data (stored in "pixels") to reflect the changed dimensions. This will result in glTexImage2D() reading unrelated program variable data as image data (at best) and/or illegal memory accesses (at worst).

Check out SDLGL_LoadTextureFromFileBestFit() from https://github.com/gpcz/OpenGL-SDL-Code-Warehouse/blob/master/SDLGLTexture.cpp for an example of how to resize an SDL_Surface to have power-of-two dimensions and how to map it to the proper OpenGL texture coordinates.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top