Search code examples
c++openglsdltexturessdl-ttf

TTF_RenderUTF8_Blended Rendering Colored Text


I'm trying to render text using SDL_ttf and openGL.

I open the font and render the text to an SDL_Surface and then attached that surface to a texture and bind it for openGL to render it.

I have googled this issue a bunch and not getting many hits which would lead me to believe I'm understand something.

The only two functions that matter since I've pretty much made a temp variable to trouble shoot this issue. They are:

SDL_Surface* CFont::BlendedUTF8Surface() {
    SDL_Surface* Surf_Text;
    SDL_Color Blah;
    Blah.r = 0;
    Blah.b = 255;
    Blah.g = 0;
    if(!(Surf_Text = TTF_RenderUTF8_Blended(pFont,TxtMsg,Blah))) {
        char str[256];
        sprintf_s(str, "? %s \n", TTF_GetError());
        OutputDebugString(str);
    }
    return Surf_Text;

}

This uses SDL_ttf to render the text to the Surf_Text surface. You can see I've maxed the blue channel. I'll talk about this in a minute. Here is the rendering

void CLabel::OnRender(int xOff,int yOff) {

    if(Visible) {
        glColor4f(1.0,1.0,1.0,1.0);
        Font.Color(FontColors.r, FontColors.g, FontColors.b); //useless I overwrote the variable with Blah to test this
        SDL_Surface* Surf_Text; 
        Surf_Text = Font.BlendedUTF8Surface();
        Text_Font.OnLoad(Surf_Text);
        Text_Font.RenderQuad(_X+xOff,_Y+yOff);
        SDL_FreeSurface(Surf_Text);
        glColor4f(0.0,0.0,0.0,1.0);
    }
}

Alright, so far from what I can tell, the problem is probably coming from the current color state and the texture environment mode.

When I render the text in this fashion, the text will change colors but it's like the R and B channels have swtiched. If I make red 255, the text is blue and if I make blue 255, the text is red. Green stays green (RGB vs BGR ?).

If I remove the glColor4f call in the rendering function, the text refused to render colored at all. Always black (I habitually set the color back to (0,0,0) everytime I render someething, so possible since the mode is modulate (R = 0 * Texture (Font) R, etc) so it will be black. Makes sense.

If I set the Texture environment to DECAL then the text renders black and a box behind the text renders the color I am trying to render the text.

I think I just don't know the correct way to do this. Anyone have any experience with SDL_ttf and openGL texture environments that could give some ideas?

Edit:

I've done some rewriting of the functions and testing the surface and have finally figured out a few things. If I use GL_DECAL the text renders the correct color and the pixel values value is 0 everywhere on the surface where it's not the red color I tried rendering (which renders with a value of 255, which is strange since red is the first channel it should have been 255^3 (or in terms of hex FF0000) at least I would expect). With DECAL, the alpha space (the white space around the text that has 0 for a pixel value) shows up the color of the current glColor() call. If I use Blended, the alpha zone disappears but my text renders as blended as well (of course) so it blends with the underlying background texture.

I guess the more appropriate question is how to I blend only the white space and not the text? My guess is that I could call a new glBlendFunc(); but I have tested parameters and I'm like a child in the woods. No clue how to get the desired result.

Solution isn't completely verified, but the format of the surface is indeed BGRA but I cannot implement this correction. I'm going to attempt to create a color swap function for this I guess.

This fix did not work. Instead of setting BGR, I thought just create a new RGB surface:

if (Surface->format->Rmask == 0x00ff0000) {
                Surface = SDL_CreateRGBSurfaceFrom(Surface->pixels, Surface->w, Surface->h, 32, Surface->pitch, Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask, Surface->format->Amask);
}

After that failed to work I tried swapping Surface->format->Bmask and Surface->format->Rmask but that had no effect either.


Solution

  • In order to handle BGR and RGB changes you can try this code to create a texture from a SDL_Surface

    int createTextureFromSurface(SDL_Surface *surface)
    {
        int texture;
        // get the number of channels in the SDL surface
        GLint  nbOfColors = surface->format->BytesPerPixel;
        GLenum textureFormat = 0;
    
        switch (nbOfColors) {
        case 1:
            textureFormat = GL_ALPHA;
            break;
        case 3:     // no alpha channel
            if (surface->format->Rmask == 0x000000ff)
                textureFormat = GL_RGB;
            else
                textureFormat = GL_BGR;
            break;
        case 4:     // contains an alpha channel
            if (surface->format->Rmask == 0x000000ff)
                textureFormat = GL_RGBA;
            else
                textureFormat = GL_BGRA;
            break;
        default:
            qDebug() << "Warning: the image is not truecolor...";
            break;
        }
    
        glEnable( GL_TEXTURE_2D );
        // Have OpenGL generate a texture object handle for us
        glGenTextures( 1, &texture );
    
        // Bind the texture object
        glBindTexture( GL_TEXTURE_2D, texture );
    
    
        // Edit the texture object's image data using the information SDL_Surface gives us
        glTexImage2D( GL_TEXTURE_2D, 0, nbOfColors, surface->w, surface->h, 0,
                      textureFormat, GL_UNSIGNED_BYTE, surface->pixels );
    
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
        return texture;
    }