Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Better font rendering  (Read 2321 times)

0 Members and 1 Guest are viewing this topic.

underww

  • Newbie
  • *
  • Posts: 34
    • View Profile
Better font rendering
« on: March 29, 2019, 03:08:18 am »
I'm always dissatisfied with SFML font rendering quality. I know it has been improved for the last several years, but it is still hard to say good enough. So I've been trying to improve it myself last few days and I found FT_LOAD_FORCE_AUTOHINT flag in sf::Font makes more bad result mostly.



Check this. The first one is default SFML text and the second one is SFML text without FT_LOAD_FORCE_AUTOHINT flag, the last one is my implementation of SFML + freetype + harfbuzz. Interestingly they all have different letter spacing even though they all have the same character size. I don't know which one is correct, but the result of my implementation is exactly the same as the original code (https://github.com/lxnt/ex-sdl-freetype-harfbuzz)

You easily find ugly rendered Korean text(the red box, only few characters here but generally I could found a lot). Maybe you can't find significant differences between English texts here, but I'm sure it's not just a problem of non-English text. Usually in small texts, SFML font rendering is not so nice.

Any ideas?

I also attach my short example code.
Sorry for my bad English :P

#include <SFML/Graphics.hpp>

#include <ft2build.h>
#include FT_FREETYPE_H

#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>

int main()
{
        const std::string filename = "fonts/KoPub Dotum Bold.ttf";
        const sf::String string = L"The quick brown fox jumps over the lazy dog. 다람쥐 헌 쳇바퀴에 타고파. 글굴귤";
        constexpr int font_size = 20;

        constexpr int width = 800;
        constexpr int height = 600;

        sf::RenderWindow window(sf::VideoMode(width, height), "SFML Text");

        sf::Font font;
        font.loadFromFile(filename);

        sf::Text text(string, font, font_size);
        text.setPosition(50.f, 50.f);

        FT_Library ft_library;
        FT_Face ft_face;

        if (FT_Init_FreeType(&ft_library) ||
                FT_New_Face(ft_library, filename.c_str(), 0, &ft_face) ||
                FT_Set_Char_Size(ft_face, font_size * 64, font_size * 64, 0, 0))
                return 1;

        hb_font_t* hb_font = hb_ft_font_create(ft_face, NULL);

        hb_buffer_t* hb_buffer = hb_buffer_create();
        hb_buffer_add_utf32(hb_buffer, string.getData(), -1, 0, -1);
        hb_buffer_guess_segment_properties(hb_buffer);

        hb_shape(hb_font, hb_buffer, NULL, 0);

        unsigned int glyph_count = hb_buffer_get_length(hb_buffer);
        hb_glyph_info_t* glyph_info = hb_buffer_get_glyph_infos(hb_buffer, NULL);
        hb_glyph_position_t* glyph_pos = hb_buffer_get_glyph_positions(hb_buffer, NULL);

        std::vector<sf::Uint8> pixelbuffer;
        pixelbuffer.resize(width * height * 4, 0);

        {
                sf::Uint8* current = pixelbuffer.data();
                sf::Uint8* end = current + width * height * 4;

                while (current != end)
                {
                        (*current++) = 255;
                        (*current++) = 255;
                        (*current++) = 255;
                        (*current++) = 0;
                }
        }

        int current_x = 50;
        int current_y = 100;

        // render
        for (unsigned int i = 0; i < glyph_count; ++i)
        {
                if (FT_Load_Glyph(ft_face, glyph_info[i].codepoint, 0))
                        return 1;

                current_x += glyph_pos[i].x_offset / 64;
                current_y += glyph_pos[i].y_offset / 64;

                FT_GlyphSlot ft_slot = ft_face->glyph;
                FT_Render_Glyph(ft_slot, FT_RENDER_MODE_NORMAL);
                FT_Bitmap ft_bitmap = ft_slot->bitmap;

                int w = ft_bitmap.width;
                int h = ft_bitmap.rows;
                int left = ft_slot->bitmap_left;
                int top = ft_slot->bitmap_top;

                const sf::Uint8* buffer = ft_bitmap.buffer;

                if (ft_bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
                        return 1;

                constexpr int padding = 1;

                w += 2 * padding;
                h += 2 * padding;

                for (int y = padding; y < h - padding; ++y)
                {
                        for (int x = padding; x < w - padding; ++x)
                        {
                                if (buffer[x - padding] > 0)
                                {
                                        std::size_t index = (current_x + x + left) + (current_y + y - top) * width - padding;
                                        pixelbuffer[index * 4 + 3] = buffer[x - padding];
                                }
                        }

                        buffer += ft_bitmap.pitch;
                }

                current_x += glyph_pos[i].x_advance / 64;
                current_y += glyph_pos[i].y_advance / 64;
        }
        //

        hb_buffer_clear_contents(hb_buffer);

        sf::Image image;
        image.create(width, height, pixelbuffer.data());

        sf::Texture texture;
        texture.loadFromImage(image);

        sf::Sprite sprite(texture);

        while (window.isOpen())
        {
                sf::Event event;

                while (window.pollEvent(event))
                {
                        if (event.type == sf::Event::Closed)
                                window.close();
                }

                window.clear();
                window.draw(text);
                window.draw(sprite);
                window.display();
        }

        hb_buffer_destroy(hb_buffer);
        hb_font_destroy(hb_font);

        FT_Done_Face(ft_face);
        FT_Done_FreeType(ft_library);

        return 0;
}
 

Edit: fixed typo
« Last Edit: March 29, 2019, 07:47:01 am by Laurent »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Better font rendering
« Reply #1 on: April 04, 2019, 10:01:32 pm »
Interesting. Thanks for taking the time to write such tests.

Would you be able to find what's different between your implementation and SFML code? Inconsistent letter spacing has already been reported in the past, and it would be awesome if we could figure out how to fix it.

Regarding the FT_LOAD_FORCE_AUTOHINT flag and glyphs quality, I don't know, I haven't followed the latest changes in sf::Font. But I'm pretty sure that this flag was added quite recently, it wasn't there in the initial implementation (as far as I remember).
Laurent Gomila - SFML developer

 

anything