Text Rendering Pipeline and Caching Strategy Based on Freetype

1.  Preface


OpenGL provides low-level support for text drawing and font manipulation, namely bitmap fonts. Each glyph is placed at an exact position in the bitmap font according to its index, and when rendering these glyphs, they are extracted per the arrangement rules and drawn to the specified position. This method is relatively easy to implement.


2.  Character Rendering Process Based on FreeType


FreeType is a software development library that can be used to load fonts and render them into bitmaps. It features open source, cross-platform compatibility, small memory footprint, support for reading and manipulating multiple font file formats, and high efficiency, among other advantages.


 2.1 Bitmap Data Generation Based on FreeType


The process of obtaining bitmap data with FreeType is, in order: initializing the library, loading the font file, setting the font size, obtaining the glyph index from character encoding, loading the glyph, and obtaining the bitmap. The specific steps are as follows:

(1) Initialize the library. Use the FT_Init_FreeType function to initialize the library, generating a Library object of type FT_Library.

(2) Load the font file. A font file may correspond to multiple font faces, and the desired face to load is obtained by its face index. The FT_New_Face function can load a font from a specified file, while the FT_New_Memory_Face function loads a font from a memory address. The obtained data is assigned to a Face object of type FT_Face to facilitate subsequent data acquisition.

(3) Set the font size. After loading the font and obtaining the Face object, you first need to use the FT_Set_Pixel_Sizes function to set the pixel size. If you are using a scalable font format, you can set size to any reasonable value; for fixed-size formats, an error will be triggered if the set size is not in the available size array of the Face object.

(4) Map character encoding to glyph index. A glyph index is the index used internally by the font file to look up glyphs. You can convert a character encoding to the corresponding glyph index through the character mapping table (charmap) provided by the font file. Usually a font file contains multiple character mapping tables to support multiple commonly used character encodings. The charmaps table of the Face object records all character mapping tables provided by the current font. You can use several predefined enumeration values to call the FT_Select_CharMap function to select a character mapping table, or you can manually traverse charmaps and call FT_Set_CharMap with a matching charmap to set the character mapping table. After completing the character mapping table setup, use the FT_Get_Char_Index function to find the glyph index corresponding to a specific character encoding in the character mapping table.

(5) Load the glyph. Once you obtain the glyph index, you can load the corresponding glyph image through the FT_Load_Glyph function. For fixed-size font formats, each glyph is stored as a bitmap; for scalable font formats, each glyph is described using a vector outline shape. The glyph image is stored in the glyph slot, and a Face object only has one glyph slot, so only one glyph corresponding to one character can be obtained at a time.

(6) Obtain the bitmap. For fixed-size font formats, the obtained glyph is already a bitmap and can be used directly; for scalable font formats, what is loaded is an outline, which must be rendered into a bitmap via the FT_Render_Glyph function before it can be used. After obtaining the bitmap data, you can access the bitmap data through the bitmap attribute of the glyph slot, and the bitmap_left and bitmap_top attributes are used to indicate the starting position.

Flowchart of Bitmap Data Generation



2.2 Creation and Drawing of Character Textures


Character texture generation and texture mapping are mainly implemented using OpenGL functions. The process includes enabling texturing, setting texture parameters, generating character textures, texture mapping, and disabling texturing. The specific steps are as follows:

(1) Enable texturing. The enabling process is: use the glGenTextures function to create a texture ID, then use the glEnable function to turn on the texture switch, then use the glBindTexture function to bind the texture ID to the current texture, and then enable the current texture mapping function.

(2) Set texture parameters. Use the glTexParameter* function to specify the filtering method for magnification and minification.

(3) Generate character texture. According to the obtained character bitmap data, use the glTexImage2D function to generate the corresponding character texture. You can set parameters for the

width, height, and RGBA or RGB format of the texture to be generated.

(4) Texture mapping. Use a combination of the glTexCoord function and the glVertex function to implement texture coordinate mapping.

(5) Disable texturing. Use the glDisable function to turn off the texture switch.

Flowchart of Texture Creation and Drawing



3.  Caching Mechanism


3.1 Bitmap Caching Mechanism


Bitmap data cache is used to store bitmap data objects of already loaded text. The size of the bitmap data cache is set during program initialization according to the number of glyphs in the font file, and all allocated memory is initialized to 0. The bitmap data cache stores and looks up bitmap data objects using character encoding as the index. A bitmap data object includes attributes such as the bitmap data of the text, a status value indicating whether its character texture is stored in the large texture, and position information within the large texture. The status value of a bitmap data object has three possible states: 0, 1, and 2. A value of 0 is the default initialization value, indicating that the text has never been drawn, and the bitmap data and position information in the bitmap data object are invalid; a value of 1 indicates that the text has been drawn and its character texture is stored in the large texture, so the bitmap data and position information in the bitmap data object are valid; a value of 2 indicates that the text has been drawn but its character texture was replaced during a texture update, so the position information in the bitmap data object is invalid, but the bitmap data itself remains valid. Through the position attribute of the bitmap data object, you can obtain the required character texture from the large texture or the required bitmap data from the bitmap data cache, which facilitates direct calling of relevant data during text drawing.


3.2 Texture Update Strategy


A large texture is generally square, and uses two attribute values to represent a coordinate on the large texture, which serves as the starting position when a new character texture is stored in the large texture. All character textures generated for the same font with the same specified font size use the same width and height. Character textures are stored in the large texture starting from the current starting position coordinate, and the starting position coordinate is then incremented with the font size as the step.

Schematic Diagram of Character Texture Storage


4.  Overall Workflow


The above design implements dual-buffer fast rendering for text, which improves the efficiency of text rendering.

Overall Workflow Chart



-End-

Author | double Jin (进)




This is a discussion topic separated from the original topic at https://www.bilibili.com/read/cv35869652/