Skip to main content
Version: 4.16

Font Caching

This section describes how to use the font cache to handle binary fonts in TouchGFX.

Read first about binary fonts.

Font Caching#

Recall that using binary fonts require the whole font to be loaded to memory. This can in some cases be undesireable if the font is large, e.g. with large Chinese fonts.

Font caching allows an application to load from external memory only the letters required to show a string. This means that the whole font does not need to reside in the addressable flash or RAM, but can be stored on a larger file system.

In the drawing below, the compiled-in font, Font2, has been replaced by the font cache. When TouchGFX is drawing a text, that uses Font2, TouchGFX will find the pointer to the CachedFont object in the font table. This special font will lookup the letters in the FontCache object.

Using a cached font

The CachedFont is setup with a pointer to the linked-in font (Font2 above). When TouchGFX asks the CachedFont for a specific letter, the cachedFont will first look in the normal Font it is replacing (Font2). This font may be an empty font, but can also be a "normal" font containing a selection of some often used letters. If the font does not contain the required letter, the CachedFont will look into the FontCache to see if the letter has been loaded from the file system.

This principle limits the amount of letters that must be cached, as we do not need to cache letters already found in the normal font.

Using the Font Cache in application code#

Before the application can install a CachedFont it must also create a FontCache, a memory buffer, and a file system reader object:

Screen1View.cpp
uint8_t fontdata[5120]; //Memory buffer for the font cache, 5Kb
FontCache fontCache;
CachedFont cachedFont; //Cached Font object
FileDataReader reader; //Filesystem reader object

The FontCache must be linked to the buffer and the reader:

Screen1View.cpp
//setup the font cache with buffer and size; and file reader object
fontCache.setMemory(fontdata, 5120);
fontCache.setReader(&reader);

Now the application can setup the font cache, initialize the CachedFont and pass it to TouchGFX.

The font cache requires a TextId to initialize a CachedFont object. The TextId is used to lookup the font that the CachedFont must point to. This secures that you are replacing the font used by the text that you have on your display:

Screen1View.cpp
//initialize the cachedFont object to the font used by T_SINGLEUSEID1
TypedText text = TypedText(T_SINGLEUSEID1);
fontCache.initializeCachedFont(text, &cachedFont);
//replace the linked in font in TouchGFX with cachedFont
TypedTextDatabase::setFont(DEFAULT, &cachedFont);

The code above can be put anywhere in the application. If the cached font is only used in a specific view, this view can be a good place to insert the code.

Caching Letters#

The font cache is still empty. Before we can show any letters they must be read from the font cache. This is done by passing an array of unicodes (a string) to the font cache. In this example we just pass the text from T_SINGLEUSEID1.

Screen1View.cpp
//cache the glyphs used by the text T_SINGLEUSEID1
Unicode::UnicodeChar* str = const_cast<Unicode::UnicodeChar*>(text.getText());
bool b = fontCache.cacheString(text, str);

The font cache will load the letters found in the str array through the reader object. The read unicodes will be linked to the font that is used by the TextId text argument.

The application is responsible for configuring the reader object to load from the correct file.

Caching Ligatures#

For languages that convert sequences of unicodes to other unicodes before displaying (e.g. Arabic and Devanagari) the above method is not good. It caches the original unicodes and not the unicodes that are displayed after conversion. This method will convert the given unicodes and cache the required unicodes (after conversion):

Screen1View.cpp
//cache the glyphs used by the text T_SINGLEUSEID1 after conversion
Unicode::UnicodeChar* str = const_cast<Unicode::UnicodeChar*>(text.getText());
bool b = fontCache.cacheLigatures(cachedFont, text, str);

Memory Usage#

The font cache can calculate the current amount used memory:

Screen1View.cpp
touchgfx_printf("Memory usage %d\n", fontCache.getMemoryUsage());

Caching GSUB Tables#

Some fonts use a GSUB table while rendering. These are only a few fonts for eastern languages, e.g. Devanagari fonts. The GSUB tables allow the font system to reorder characters and substitute sequences of characters for other "combination" characters.

The Font Cache can load this GSUB table from the file system. If it is not loaded, the font is not displayed correctly as the GSUB table is then not available to the text rendering system.

The GSUB table is loaded by supplying an extra argument when initializing the cached font:

Screen1View.cpp
//initialize the cachedFont and load the GSUB table
text = TypedText(T_SINGLEUSEID1);
fontCache.initializeCachedFont(text, &cachedFont, true);

Implementing the Font File Reader#

The FileDataReader class used in the above example code is not included in TouchGFX as it is dependant on the operating system you are using.

Here is an example for normal "stdio" compatible file systems.

Screen1View.cpp
class FileDataReader : public FontDataReader
{
public:
virtual ~FileDataReader() { }
virtual void open()
{
fp = fopen("Font_verdana_20_4bpp.bin", "rb");
if (!fp)
{
touchgfx_printf("Unable to open font file!!!\n");
}
}
virtual void close()
{
fclose(fp);
}
virtual void setPosition(uint32_t position)
{
fseek(fp, position, SEEK_SET);
}
virtual void readData(void* out, uint32_t numberOfBytes)
{
fread(out, numberOfBytes, 1, fp);
}
private:
FILE* fp;
};

The FileDataReader class implements the FontDataReader interface from FontCache.hpp:

FontCache.hpp
class FontDataReader
{
public:
virtual ~FontDataReader() { }
virtual void open() = 0;
virtual void close() = 0;
virtual void setPosition(uint32_t position) = 0;
virtual void readData(void* out, uint32_t numberOfBytes) = 0;
};