rougier / freetype-gl

OpenGL text using one vertex buffer, one texture and FreeType
Other
1.65k stars 266 forks source link

Text Displayed Incorrectly with Large Font Sizes #156

Open silverness2 opened 7 years ago

silverness2 commented 7 years ago

Hi,

I am working with a high res display, and am testing different font sizes. I've run into a strange problem where font sizes greater than 655 will render the text incorrectly.

As an example, I took the demo/markup.c program and stripped it down so it is only displaying one line of markup text. Following is the init() method from that code. I marked areas where I changed the code with a comment called CHANGED.

void init()
{
    text_shader = shader_load("shaders/text.vert", "shaders/text.frag");

    // CHANGED
    font_manager = font_manager_new( 8192, 8192, LCD_FILTERING_ON );
    buffer = text_buffer_new( );

    vec4 white  = {{1.0, 1.0, 1.0, 1.0}};
    vec4 yellow = {{1.0, 1.0, 0.0, 1.0}};
    vec4 none   = {{1.0, 1.0, 1.0, 0.0}};

    char *f_normal   = match_description((const unsigned char*)"FreeSans");

    // markup_t describes text properties.
    markup_t normal = {
        family  : f_normal, // a font family, such as "normal", "sans", "serif", or "monospace"
        size    : 24.0,     // font size
        bold    : 0,
        italic  : 0,
        spacing : 0.0,      // spacing between letters
        gamma   : 2.,       // gamma correction
        foreground_color    : white, // text color
        background_color    : none,
        outline             : 0,     // whether outline is active
        outline_color       : none,
        underline           : 0,     // whether underline is active
        underline_color     : white,
        overline            : 0,     // whether overline is active
        overline_color      : white,
        strikethrough       : 0,     // whether strikethrough is active
        strikethrough_color : white,
        font : 0            // pointer on the corresponding font (family/size/bold/italic)
    };

    // CHANGED
    markup_t big = normal;
    big.size = 655.0;
    big.foreground_color = yellow;

    normal.font = font_manager_get_from_markup( font_manager, &normal );
    big.font = font_manager_get_from_markup( font_manager, &big );

    // CHANGED
    vec2 pen = {{0, 723}};
    text_buffer_printf( buffer, &pen,
                        &big, "FREE-TYPE",
                        NULL );

    // CHANGED
    vec4 bounds = text_buffer_get_bounds(buffer, &pen);
    printf("for font size: %f\n", big.size);
    printf("bounds width is: %f\n", bounds.width);
    printf("bounds height is: %f\n", bounds.height);
    printf("pen x is: %f\n", pen.x);
    printf("pen y is: %f\n", pen.y);

    glGenTextures( 1, &font_manager->atlas->id );
    glBindTexture( GL_TEXTURE_2D, font_manager->atlas->id );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
    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, GL_RGB, font_manager->atlas->width,
                  font_manager->atlas->height, 0, GL_RGB, GL_UNSIGNED_BYTE,
                  font_manager->atlas->data );

    text_buffer_align( buffer, &pen, ALIGN_CENTER );

    mat4_set_identity( &projection );
    mat4_set_identity( &model );
    mat4_set_identity( &view );
}

In main(), I altered the window width and height, as follows:

    ...

    // CHANGED
    int winW = 3727;
    int winH = 723;
    window = glfwCreateWindow( winW, winH, argv[0], NULL, NULL );

    ...

    // CHANGED
    reshape( window, winW, winH );

In my first test, I set the font size (big.size) to 655.0. Following are the bounds and pen after rendering the text via text_buffer_printf:

for font size: 655.000000
bounds width is: 3606.406250
bounds height is: 720.500000
pen x is: 3606.406250
pen y is: 133.500000

Here, the rendering is correct:

fontsize655

In the second test, I set the font size (big.size) to 670. Following are the bounds and pen after rendering via text_buffer_printf (note that for this test, I did not change the window dimensions, pen origin, or anything else compared to the first test):

for font size: 670.000000
bounds width is: 3689.015625
bounds height is: 16.109985
pen x is: 3689.015625
pen y is: 709.820007

Here, we can see the bounds height and the pen y are incorrect and the drawing is incorrect (you can barely see some text rendered at the top of the image):

fontsize670

rougier commented 7 years ago

Weird.

Could you test if making the window height bigger changes anything? Also, could you print the markup.font.ascender and markup.font.descender (after font has been loaded)?

silverness2 commented 7 years ago

Hi,

The additional ascender/descender info for the correctly rendered test above (where font size is 655.0):

for font size: 655.000000
bounds width is: 3606.406250
bounds height is: 720.500000
pen x is: 3606.406250
pen y is: 133.500000
markup.font.ascender is: 589.500000
markup.font.descender is: -131.000000

The additional ascender/descender info for the incorrectly rendered test above (where font size is 670.0):

for font size: 670.000000
bounds width is: 3689.015625
bounds height is: 16.109985
pen x is: 3689.015625
pen y is: 709.820007
markup.font.ascender is: 13.180000
markup.font.descender is: -2.930000

For the following images, I used a font size of 670 and a window width of 3727 (same as the second example above), but I changed the window height from 723 to the following.

Here is the image for window height = 800: windowheight-800

for font size: 670.000000
bounds width is: 3689.015625
bounds height is: 16.109985
pen x is: 3689.015625
pen y is: 709.820007
markup.font.ascender is: 13.180000
markup.font.descender is: -2.930000

Here is the image for window height = 900: windowheight-900

for font size: 670.000000
bounds width is: 3689.015625
bounds height is: 16.109985
pen x is: 3689.015625
pen y is: 709.820007
markup.font.ascender is: 13.180000
markup.font.descender is: -2.930000
rougier commented 7 years ago

Probably something wrong when ascender/descender are computed in texture font. It starts at line 251. Could you also add debug information to display metrics.ascender and metrics.descender? Most probably there is an overflow somewhere but I don't see any reason this would happend for size 670 and not for size 655.

silverness2 commented 7 years ago

I noticed that any font size greater than 655.0 causes the rendering error.

Here are the metrics for font size = 655.0:

metrics.ascender is: 3744000
metrics.descender is: -832000
for font size: 650.000000
bounds width is: 3578.906250
bounds height is: 715.000000
pen x is: 3578.906250
pen y is: 138.000000
markup.font.ascender is: 585.000000
markup.font.descender is: -130.000000

Here are the metrics for font size = 670.0:

metrics.ascender is: 84352
metrics.descender is: -18752
for font size: 670.000000
bounds width is: 3689.015625
bounds height is: 16.109985
pen x is: 3689.015625
pen y is: 709.820007
markup.font.ascender is: 13.180000
markup.font.descender is: -2.930000
rougier commented 7 years ago

Maybe part of the problem comes from the texture_font_init() function that multiply the actual size by 100 to obtain high precision ascender/descender. However, the char size should be encoded in 26.6 fixed precision format. Since we also multiply the size by HRES, we may overflow the representation even if it is theoretically not the case.

I guess that if you replace 100.f with 10.f in this function, it should work as expected.

silverness2 commented 7 years ago

Many apologies for my delay in responding!!

I just completed a test with your suggestion of replacing 100.f with 10.f in the texture_font_init() function, and it works as you predicted.

These are the lines I replaced with 10.f:

if (!texture_font_load_face(self, self->size * 10.f, &library, &face))
self->ascender = (metrics.ascender >> 6) / 10.0;
self->descender = (metrics.descender >> 6) / 10.0;
self->height = (metrics.height >> 6) / 10.0;

Here is the image for the original window height (from the very first test) of (3727, 723) and a font size = 670.0 (the one that was creating the rendering error): freetype670-tb10

Here are the new metrics for font size = 670.0:

metrics.ascender is: 13824
metrics.descender is: -3072
metrics.ascender is: 385920
metrics.descender is: -85760
for font size: 670.000000
bounds width is: 3689.015625
bounds height is: 737.000000
pen x is: 3689.015625
pen y is: 120.000000
markup.font.ascender is: 603.000000
markup.font.descender is: -134.000000