Thread: TTF_Font to GL Display List Kerning issue

  1. #1
    Registered User
    Join Date
    May 2012
    Posts
    12

    TTF_Font to GL Display List Kerning issue

    [Edit] Forgot to mention that although the font looks like it has an outline, that's just part of the font. The actual true-type font looks like that, haha. Also, I can end up with it looking like this http://i.imgur.com/x6jl5.jpg, which is better, if I remove the first glTranslate to "minx", but the spacing still looks wrong. [/Edit]

    I'm working on font code in my SDL_GL application, using SDL_TTF. The code to generate and call the display list is working, but the spacing looks off: http://i.imgur.com/7Fan7.jpg

    The relevant section of code is this section:

    Code:
        glNewList(list+ch, GL_COMPILE);
        glBindTexture(GL_TEXTURE_2D, tex[ch]);
        int minx, maxx, miny, maxy, advance;
        TTF_GlyphMetrics(Font, ch, &minx, &maxx, &miny, &maxy, &advance);
        glPushMatrix();
        glTranslated(minx, miny, 0);
        glBegin(GL_QUADS);
            glTexCoord2f(0, 0); glVertex2i(0, 0);
            glTexCoord2f(1, 0); glVertex2i(sText->w, 0);
            glTexCoord2f(1, 1); glVertex2i(sText->w, sText->h);
            glTexCoord2f(0, 1); glVertex2i(0, sText->h);
        glEnd();
        glPopMatrix();
        glTranslated(advance, 0, 0);
        glBindTexture(GL_TEXTURE_2D, 0);
        glEndList();
    Is the advance part of glyph metrics in SDL_TTF just not good enough? I tried switching "glTranslated" to "glTranslatef", but TTF_GlyphMetrics returns ints and not floats so it doesn't change anything.
    Last edited by Alexander Edgar; 05-08-2012 at 04:30 PM.

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Yeah, TTF_GlyphMetrics is just not good enough. From its docs (http://www.libsdl.org/projects/SDL_t..._ttf_38.html):
    Any glyph based rendering calculations will not result in accurate kerning between adjacent glyphs. (see section Kerning)
    From SDL_ttf 2.0.10: Glossary:
    Kerning Kerning is the process of spacing adjacent characters apart depending on the actual two adjacent characters. This allows some characters to be closer to each other than others. When kerning is not used, such as when using the glyph metrics advance value, the characters will be spaced out at a constant size that accomodates all pairs of adjacent characters. This would be the maximum space between characters needed. There's currently no method to retrieve the kerning for a pair of characters from SDL_ttf, However correct kerning will be applied when a string of text is rendered instead of individual glyphs.
    Here's how I normally do SDL text in OpenGL. I create a class/struct/whatever that knows the text it's supposed to render; the first time it's called, it pre-renders itself to an SDL surface and then converts the SDL surface into an OpenGL texture. (At this point I think the SDL surface can be freed.) Then every time it's asked to render, it just displays the OpenGL texture in the right place.

    The one additional wrinkle to this is that the dimensions of OpenGL textures are supposed to be a power of two. So you have to round upwards and find the next possible texture size, render the text (presumably with transparency!), then scale the coordinates you're given so that the text appears the size you want it to be -- and the rest of the texture is perhaps stretching off the side of the screen, but that doesn't matter because it's transparent. Whenever the text changes, of course, the text object is marked as dirty and the regeneration process is automatically done all over again.

    Alternatively you can just make the textures big enough, as you have done, and you don't have to worry about the power-of-two business.

    Sounds complicated? You bet.
    Code:
    void Text::preRender() {
        const std::string &data = /*...*/;
        if(data.empty()) {
            texture = -1;
            return;
        }
        
        SDL_Color c = /*...*/;
        TTF_Font *font = /*...*/;
        
        // note: we rely on this function creating 32-bit images,
        // as its documentation describes.
        SDL_Surface *first = TTF_RenderText_Blended(font, data.c_str(), c);
        
        int w = nextPowerOf2(first->w);
        int h = nextPowerOf2(first->h);
        
        // save the ratio between rendered text and actual texture dimensions
        this->widthFactor = double(w) / first->w;
        this->heightFactor = double(h) / first->h;
        
        SDL_Surface *second = SDL_CreateRGBSurface(
            SDL_SWSURFACE, w, h,
            32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000);
        
        // copy data from first to second, translating to new format
        SDL_BlitSurface(first, NULL, second, NULL);
        
        glEnable(GL_TEXTURE_2D);
        
        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);
        
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        
        // we assume this copies the pixels from second, and later free second
        glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_BGRA, 
            GL_UNSIGNED_BYTE, second->pixels);
        
        glDisable(GL_TEXTURE_2D);
        
        SDL_FreeSurface(first);
        SDL_FreeSurface(second);
        
        dirty = false;
    }
    
    void Text::render() {
        if(dirty) {
            preRender();
        }
        
        if(texture == unsigned(-1)) {
            return;  // empty text, or error in pre-rendering
        }
        
        WidgetPoint corner = getBoundingRect().getCorner();
        WidgetPoint dimensions = getBoundingRect().getDimensions();
        
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, texture);
        glColor3f(1.0f, 1.0f, 1.0f);
        
        // comment this out to disable font transparency
        glEnable(GL_BLEND);
        
        glBlendFunc(GL_ONE, GL_ONE);
        //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        //glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
        
        glBegin(GL_QUADS);
        
        double width = dimensions.getX() * widthFactor;
        double height = dimensions.getY() * heightFactor;
        
        // scale dimensions to take the larger power-of-2 texture into account
        // this way it looks the same no matter how big the texture actually is
        WidgetPoint topLeft = corner;
        WidgetPoint lowerLeft = corner;
        WidgetPoint topRight = corner;
        WidgetPoint lowerRight = corner;
        lowerLeft.addY(height);
        lowerRight.addY(height);
        topRight.addX(width);
        lowerRight.addX(width);
        
        glTexCoord2i(0, 0); WidgetRenderer::glVertex(topLeft);
        glTexCoord2i(1, 0); WidgetRenderer::glVertex(topRight);
        glTexCoord2i(1, 1); WidgetRenderer::glVertex(lowerRight);
        glTexCoord2i(0, 1); WidgetRenderer::glVertex(lowerLeft);
        
        glEnd();
        
        // back to "normal" blending
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        
        glDisable(GL_TEXTURE_2D);
        glDisable(GL_BLEND);
    }
    Hope that's at least a little bit understandable . . . .

    Do some google searches on this, I did extensive research once and there are certainly alternatives. This is just one method. You can find something simpler, although this code came out of an actual project I worked on, so I can say that it does work!

    It would be nice if there was some code in SDL_ttf that handled this for you, but alas, I don't think there is. Well, good luck.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 13
    Last Post: 05-02-2012, 04:17 PM
  2. Display a hex value in a list box?
    By klipseracer in forum Windows Programming
    Replies: 5
    Last Post: 01-27-2008, 10:46 AM
  3. Nested for loop...search & display a list within a list
    By chadsxe in forum C++ Programming
    Replies: 13
    Last Post: 07-20-2005, 01:34 PM
  4. Dual Display issue
    By psychopath in forum Tech Board
    Replies: 8
    Last Post: 02-12-2005, 12:02 PM
  5. Display list not displaying?
    By psychopath in forum Game Programming
    Replies: 5
    Last Post: 09-19-2004, 06:47 PM