1

I've been attempting to render text onto an openGL window using SDL and the SDL_TTF library on windows XP, VS2010.
Versions:
SDL version 1.2.14
SDL TTF devel 1.2.10
openGL (version is at least 2-3 years old).

I have successfully created an openGL window using SDL / SDL_image and can render lines / polygons onto it with no problems.

However, moving onto text it appears that there is some flaw in my current program, I am getting the following result when trying this code here

enter image description here

for those not willing to pastebin here are only the crutial code segments:

void drawText(char * text) {
    glLoadIdentity();
    SDL_Color clrFg = {0,0,255,0}; // set colour to blue (or 'red' for BGRA)
    SDL_Surface *sText = TTF_RenderUTF8_Blended( fntCourier, text, clrFg );
    GLuint * texture = create_texture(sText);
    glBindTexture(GL_TEXTURE_2D, *texture);
    // draw a polygon and map the texture to it, may be the source of error
    glBegin(GL_QUADS); {
        glTexCoord2i(0, 0); glVertex3f(0, 0, 0);
        glTexCoord2i(1, 0); glVertex3f(0 + sText->w, 0, 0);
        glTexCoord2i(1, 1); glVertex3f(0 + sText->w, 0 + sText->h, 0);
        glTexCoord2i(0, 1); glVertex3f(0, 0 + sText->h, 0);
    } glEnd();
    // free the surface and texture, removing this code has no effect
    SDL_FreeSurface( sText );
    glDeleteTextures( 1, texture );
}

segment 2:

// create GLTexture out of SDL_Surface
GLuint * create_texture(SDL_Surface *surface) {
    GLuint texture = 0;

    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    // The SDL_Surface appears to have BGR_A formatting, however this ends up with a 
    // white rectangle no matter which colour i set in the previous code.
    int Mode = GL_RGB;

    if(surface->format->BytesPerPixel == 4) {
        Mode = GL_RGBA;
    }

    glTexImage2D(GL_TEXTURE_2D, 0, Mode, surface->w, surface->h, 0, Mode, 
                 GL_UNSIGNED_BYTE, surface->pixels);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    return &texture;
}

Is there an obvious bit of code I am missing?
Thank you for any help on this subject.
I've been trying to learn openGL and SDL for 3 days now, so please forgive any misinformation on my part.


EDIT:

I notice that using
TTF_RenderUTF8_Shaded
TTF_RenderUTF8_Solid

Throw a null pointer exception, meaning that there is an error within the actual text rendering function (I suspect), I do not know how this means TTF_RenderUTF8_Blended returns a red square but I suspect all troubles hinge on this.

Serdalis
  • 10,296
  • 2
  • 38
  • 58

4 Answers4

3

I think the problem is in the glEnable(GL_TEXTURE_2D) and glDisable(GL_TEXTURE_2D) functions which must be called every time the text is painted on the screen.And maybe also the color conversion between the SDL and GL surface is not right. I have combined create_texture and drawText into a single function that displays the text properly. That's the code:

void drawText(char * text, TTF_Font* tmpfont) {
SDL_Rect area;
SDL_Color clrFg = {0,0,255,0};
SDL_Surface *sText = SDL_DisplayFormatAlpha(TTF_RenderUTF8_Blended( tmpfont, text, clrFg ));
area.x = 0;area.y = 0;area.w = sText->w;area.h = sText->h;
SDL_Surface* temp = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,sText->w,sText->h,32,0x000000ff,0x0000ff00,0x00ff0000,0x000000ff);
SDL_BlitSurface(sText, &area, temp, NULL);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sText->w, sText->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp->pixels);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS); {
    glTexCoord2d(0, 0); glVertex3f(0, 0, 0);
    glTexCoord2d(1, 0); glVertex3f(0 + sText->w, 0, 0);
    glTexCoord2d(1, 1); glVertex3f(0 + sText->w, 0 + sText->h, 0);
    glTexCoord2d(0, 1); glVertex3f(0, 0 + sText->h, 0); 
} glEnd();
glDisable(GL_TEXTURE_2D);
SDL_FreeSurface( sText );
SDL_FreeSurface( temp );
} 

screenshot

I'm initializing OpenGL as follows:

int Init(){
glClearColor( 0.1, 0.2, 0.2, 1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho( 0, 600, 300, 0, -1, 1 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if( glGetError() != GL_NO_ERROR ){
    return false;
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);
} 
axel22
  • 32,045
  • 9
  • 125
  • 137
slaviber
  • 358
  • 2
  • 10
  • Fantastic! This helped me as well. I was searching what would happen since the surfaces with text aren't usually -to the power of 2- dimensions. It could lead to an incompatibility issue. Thoughts? – lb. Mar 12 '12 at 04:44
2

EDIT

Okay, I finally took the time to put your code through a compiler. Most importantly, compiler with -Werror so that warning turn into errors

GLuint * create_texture(SDL_Surface *surface) {
    GLuint texture = 0;

    /*...*/

    return &texture;
}

I didn't see it first, because that's something like C coder's 101 and is quite unexpected: You must not return pointers to local variables!. Once the functions goes out of scope the pointer returned will point to nonsense only. Why do you return a pointer at all? Just return a integer:

GLuint create_texture(SDL_Surface *surface) {
    GLuint texture = 0;

    /*...*/

    return texture;
}

Because of this you're also not going to delete the texture afterward. You upload it to OpenGL, but then loose the reference to it.


Your code misses a glEnable(GL_TEXTURE_2D) that's why you can't see any effects of texture. However your use of textures is suboptimal. They way you did it, you recreate a whole new texture each time you're about to draw that text. If that happens in a animation loop, you'll

  1. run out of texture memory rather soon
  2. slow it down significantly

(1) can be addressed by not generating a new texture name each redraw

(2) can be addresses by uploading new texture data only when the text changes and by not using glTexImage2D, but glTexSubImage2D (of course, if the dimensions of the texture change, it must be glTexImage2D).


EDIT, found another possible issue, but first fix your pointer issue.

You should make sure, that you're using GL_REPLACE or GL_MODULATE texture environment mode. If using GL_DECAL or GL_BLEND you end up with red text on a red quad.

Community
  • 1
  • 1
datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • my code includes `glEnable(GL_TEXTURE_2D)` on line 94 of the pasted code, and I am freeing the texture each time the text has been drawn, so no memory leakage, thanks for the `glTexSubImage2D` hint however when added it causes a white rectangle, no matter the `RGB` / `BGR` – Serdalis Oct 20 '11 at 05:53
  • +1 for the pointer error. However, the problem is disabled blending. – arul Oct 20 '11 at 10:13
  • unfortunatly that's not the issue, you are correct however, but I thought of something like this and extracted all the code into the drawText method, the same error happened, also, that method is returning a `GLuint array` (allocated in heap) which will point to a texture already in memory (hence the `glGenTextures(1, &texture);` line) so nothing is being deleted. I'll start looking more along those lines though, but since the square changes colour when I change the text colour I suspect nothing is being deleted. – Serdalis Oct 20 '11 at 10:13
  • @Serdalis: That method is returning GLuint, which is a fancy name for an `unsigned int`. You are returning an address of temporary variable, which is undefined. It may work some time, but some time later it might hit you in the back. – arul Oct 20 '11 at 10:22
  • Ok, thank you both for that, This may not solve the problem but I'll keep an eye out for that in the future, the tutorial I copy pasted from might want to update this :S – Serdalis Oct 20 '11 at 10:46
  • Very good suggestions, I have tried setting `glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);` and `GL_REPLACE` but to no avail, I am however getting quite a few segfaults from the `TTF_RenderUTF8_Solid` function, I suspect something is seriously wrong there, I have updated the code in pastebin with all your and v01d's suggestions, the best I can get is a rainbow rectangle (rainbow pixels on white background). This is truly weird. (changes on lines `79`, `80`, all of `drawText` function). – Serdalis Oct 20 '11 at 11:15
  • after printing out the pixels generated by the TTF function, I've concluded that the square is actually whats being generated, openGL does not seem to be at blame, I'll try a different text renderer, thank you for your time however, you have taught me a lot about openGL. If you are curious I've included my printing code in the pastebin for you to conduct your own tests if you wish. Thank you for your help :) – Serdalis Oct 20 '11 at 11:53
2

I think you should just add glEnable(GL_BLEND), because the code for the text surface says TTF_RenderUTF8_Blended( fntCourier, text, clrFg ) and you have to enable the blending abilities of opengl.

v01d
  • 1,457
  • 1
  • 11
  • 22
  • no these are completely different operations, the blended function just uses alpha to 'darken' the edges of the text, making it look nicer, while GL_BLEND mixes colour values with global colour values etc etc. http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf_52.html – Serdalis Oct 20 '11 at 07:46
  • @Serdalis: No you are wrong. I hear you say 'Alpha', the correct term is 'Alpha Blending'. Even the page you linked clearly states that "...This results in a surface with alpha transparency". http://www.opengl.org/resources/faq/technical/transparency.htm – arul Oct 20 '11 at 10:06
  • I have tried including this and it does not work, this is not the problem or the solution, read part 15.080 of the page I linked for what I suspect this function is doing. – Serdalis Oct 20 '11 at 10:38
  • Thank you both for your time, I've decided to go with a different text renderer with openGL, one that works hopefully. – Serdalis Oct 20 '11 at 11:54
2

There was leaking memory of of the function in my previous post and the program was crashing after some time... I improved this by separating the texture loading and displaying:

The first function must be called before the SDL loop.It loads text string into memory:

Every string loaded must have different txtNum parameter

GLuint texture[100];
SDL_Rect area[100]; 
void Load_string(char * text, SDL_Color clr, int txtNum, const char* file, int ptsize){
    TTF_Font* tmpfont;
    tmpfont = TTF_OpenFont(file, ptsize); 
    SDL_Surface *sText = SDL_DisplayFormatAlpha(TTF_RenderUTF8_Solid( tmpfont, text, clr ));
    area[txtNum].x = 0;area[txtNum].y = 0;area[txtNum].w = sText->w;area[txtNum].h = sText->h;
    glGenTextures(1, &texture[txtNum]);
    glBindTexture(GL_TEXTURE_2D, texture[txtNum]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, sText->w, sText->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, sText->pixels);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    SDL_FreeSurface( sText );
    TTF_CloseFont(tmpfont);
 }

The second one displays the string, must be called in the SDL loop:

void drawText(float coords[3], int txtNum) {
     glBindTexture(GL_TEXTURE_2D, texture[txtNum]);
     glEnable(GL_TEXTURE_2D);
     glBegin(GL_QUADS); {
     glTexCoord2f(0, 0); glVertex3f(coords[0], coords[1], coords[2]);
     glTexCoord2f(1, 0); glVertex3f(coords[0] + area[txtNum].w, coords[1], coords[2]);
     glTexCoord2f(1, 1); glVertex3f(coords[0] + area[txtNum].w, coords[1] + area[txtNum].h, coords[2]);
     glTexCoord2f(0, 1); glVertex3f(coords[0], coords[1] + area[txtNum].h, coords[2]); 
     } glEnd();
     glDisable(GL_TEXTURE_2D);
}

enter image description here

slaviber
  • 358
  • 2
  • 10