Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Render FreeType text with flipped ortho, difference between top and baseline of glyph

I am working on a project where I implement a FreeType rendering object to draw text of which the rendering environment is specified with an orthographic projection matrix:

glm::ortho(0, Width, Height, 0);

This makes sure the coordinates are similar to standard GUI systems with (0,0) being the top-left part of the window instead of the bottom-left.

However when rendering using FreeType, things get difficult, because FreeType operates with their origin at the bottom-left of a glyph (minus the descender). My issue is similar to https://stackoverflow.com/questions/25353472/render-freetype-gl-text-with-flipped-projection but no answer was yet provided and his solution was not to my liking (the used library is also slightly different, I assume he is using a wrapper).

So I render my text as follows:

renderText("Testing 123 if text performs sufficiently", 0.0f, 0.0f, 1.0f, 1.0f);

Of which renderText function contains:

renderText(const GLchar *text, GLfloat x, GLfloat y, GLfloat sx, GLfloat sy)
{
    [...]
    GLfloat xpos = x + glyph->bitmap_left * sx; 
    GLfloat ypos = y - glyph->bitmap_top * sy; 
    GLfloat w = glyph->bitmap.width * sx;
    GLfloat h = glyph->bitmap.rows * sy;
    // Update VBO
    GLfloat vertices[4][4] = {
        { xpos,     ypos,       0, 0 },
        { xpos + w, ypos,       1, 0 },
        { xpos,     ypos + h,   0, 1 },
        { xpos + w, ypos + h,   1, 1 }
    };
    [...]
}

If I render it like this, it will render the text below the y coordinate of 0 so it won't be visible unless I add an offset to the y coordinate. So looking at FreeType's glyph metrics:

glyph metrics from FreeType

I want to offset the y position by a positive amount equal to the difference between the origin and the top of the glyph image so it always neatly renders the text at my given position. Looking at the image I believe this to be the yMax value so I added the following statement to the code before updating the VBO:

ypos += (glyph->face->bbox.yMax >> 6) * sy; 

Which seemed to fix the issue when I loaded the FreeType glyphs with font size 24, but as soon as I tried to use different font sizes it failed to work as this image shows:

font size differences

As you can see, it clearly doesn't work as I thought it would. I've been thouroughly searching through FreeType's documentation if I was missing something, but I could not find it. Am I using the wrong metrics (using Ascender didn't work as well)?

like image 805
Djemon Avatar asked Dec 14 '14 12:12

Djemon


1 Answers

I want to offset the y position by a positive amount equal to the difference between the origin and the top of the glyph image so it always neatly renders the text at my given position. Looking at the image I believe this to be the yMax value so I added the following statement to the code before updating the VBO:

    ypos += (glyph->face->bbox.yMax >> 6) * sy;

In actuality, yMax is not what you are interested in. You could use yMax - yMin to find the height of your glyph, but that is really all it is good for.

From the FreeType 2 API documentation, FT_GlyphSlotRec::bitmap_top is:

The bitmap's top bearing expressed in integer pixels. Remember that this is the distance from the baseline to the top-most glyph scanline, upwards y coordinates being positive.

Look at the image you included in your question again, that is effectively bearingY. Your problem is that you are subtracting this value from your ypos when you should not be. You do need the value as I will explain below, but you definitely do not want to subtract it.

If you eliminate bitmap_top from your calculation of ypos you get the following:

False alignment

Which is obviously incorrect because it ignores differences in ascent between each character in your string.

Now, take a close look at the following correctly aligned diagram:

In the diagram above, I have illustrated your string's top-most line in red, bottom-most in green and the baseline for all of your glyphs in grey.

As you can see, the capital letter 'T' has the greatest ascent and this generalization holds true for most fonts. Directly below the red line, I have illustrated the difference in ascent between capital 'T' and the current letter as the yellow area. This is the important quantity that you must calculate to properly align your string.

The yellow region in the correctly aligned figure above can be calculated thus:

    Chars['T'].bitmap_top - glyph->bitmap_top

If you stop subtracting glyph->bitmap_top from ypos and add the value above, you should get the same results as the correctly aligned diagram.


For bonus points, if you want to align your text to the bottom of the screen, the concept is very similar only you are interested in the difference between the character with the greatest descent (often lowercase 'g') and the current character. That is the distance between the grey baseline and the green line and can be expressed as height - bearingY in your glyph metrics figure.

You should be able to compute descent using this:

(glyph->metrics.height >> 6) - glyph->bitmap_top // bitmap_top is in integer coords
like image 142
Andon M. Coleman Avatar answered Sep 18 '22 06:09

Andon M. Coleman