주요 내용으로 건너뛰기

Bitmap Font Compression

TouchGFX supports bitmap font compression starting from version 4.25.

The bitmap font compression feature allows bitmap fonts in 4 BPP to be compressed. The target is to reduce the size of the font data while maintaining the rendering performance.

It is very simple to enable font compression in the Designer. You just select "4 - compressed" as the "Bpp" for your typography in the Designer.

Selecting compressed 4 BPP

Two types of applications can benefit from this. An application with many thousand letters, for example Chinese letters, or an application with very large letters. These applications gain a lot. Whereas an application using only English letters in size 20 will not gain many bytes, as the font data is already not large to begin with.

The compression rate depends on the size of the letters, the font size. Small fonts compress worse than larger fonts. You can expect compression from 30% to 70% of the original.

The letters are decompressed to a cache buffer before drawing. If the letter is not in the cache we have to pay for the decompression before rendering. The compression algorithm used (run-length type) is very easy to decompress, so this will normally not be a problem. If the letter it is already in the cache the performance of rendering will be a bit better than normal, because rendering from SRAM is faster than from external flash. The cache size can be configured in the Designer. See below.

If you are drawing the same letter many times, because it is repeated in the text or because you redraw the text as part of an animation or scrolling, the cache will in many cases eliminate the cost of decompressing.

Note
The cache size must be big enough to hold the largest letter of the text you are drawing.

If the cache is not big enough, the application will stop.

See instructions here.

If the platform offers hardware accelerated drawing this is also used with compressed fonts.

An alternative to the compressed fonts are vector fonts. These are typically smaller than the compressed fonts when more than one size is used, but comes with a bigger performance hit.

Read about vector fonts here.

Performance

In this section we will examine an application that uses a lot of Chinese letters and an application with large numbers.

For the first example we will create a font using NotoSansCJKsc-Black.otf in size 28. We include the range of characters 0x4E00 to 0x5E00 (plus a few extra used in the text). This gives us 4165 characters. A real Chinese application could of course include even more characters.

The application shows a text of 24 letters:

Chinese characters in size 28

The second application uses Verdana in size 40 and size 140:

Verdana text in size 40 and 140

We will check the size of the font data and the performance of drawing the letters.

Font data size

The table below shows the font data size (sizes in bytes):

FontSizeCharactersUncompressed sizeCompressed sizeSavingCompression Ratio
NotoSansCJKsc-Black.otf284,1161,568,599917,170651,4291.7
Verdana.ttf409527,36912,78914,5802.1
Verdana.ttf1401140,4088,21132,1974.9

We see that compression of the Chinese characters saved more than half a megabyte of data. This can be large enough to reduce the size of the external flash and thus reduce the BOM cost.

The compression of the Verdana fonts did not save a similar amount of bytes even though the compression ratio was higher. Primarily because the number of characters is so much lower.
The sum of the data for the two Verdana fonts is below 22Kb. This means that the font data can fit in the internal flash of very small devices.

If we generate the Noto font as a vector font, the size of the font data is 1,509,236 bytes. That is slightly less than the uncompressed font size, but larger than the compressed bitmap font. The big size gain with vector fonts comes when you use the vector font in multiple sizes, as the data is shared for all sizes of a font.

Rendering

We will now check the performance of rendering compressed letters. As we will see the cache size is important. If the application repeatedly draws the same letters and the cache is big enough to hold them, the performance is close to uncompressed fonts and sometimes better.

We will start with the Chinese application and a cache size of 10,000 bytes. This is enough to hold the all used letters.

We measure the render time of redrawing the whole screen including the background image.

TestCache sizeRender time /ms
Background Image-5.28 ms
Uncompressed Noto-7.57 ms
Compressed Noto100007.55 ms
Compressed Noto50009.41 ms
Compressed Noto20009.42 ms

The cache requirement for holding all the letters shown is around 8000 bytes. If the cache is bigger than this and the characters have been decompressed in the first frame, the rendering can be done without decompressing again in all future frames. When the cache is smaller (5,000 or 2,000 bytes) we have to repeatedly decompress because the cache was not big enough to hold all characters. The performance is lower but still usable.

We will now look at the second application. This time only the large number is redrawn in every frame. The numbers are around 4,000 bytes each, so a cache of 10,000 bytes is large enough to hold two numbers. A cache of e.g. 5000 bytes can only hold one letter and we have to decompress repeatedly.

TestCache sizeRender time /ms
Background Image-1.44 ms
Uncompressed Verdana-2.46 ms
Compressed Verdana100002.45 ms
Compressed Verdana50003.57 ms

We see again, that if the cache is large enough, the performance is unchanged, but we get a penalty otherwise.

As a final example we will use a more synthetic application just showing a lot of 'A' characters:

Verdana text in size 40

TestCache sizeRender time /ms
Background Image-5.19 ms
Uncompressed Verdana-16.51 ms
Compressed Verdana1000016.45 ms
Compressed Verdana100016.46 ms

The performance is identical in all configurations. Since we are only drawing a single character 'A' it is enough to have a very small cache.

The cache must be big enough to hold the unique characters drawn in the frame. In this case just the 'A'.

Conclusion

To sum up, compressed fonts have a performance identical to the uncompressed fonts if the cache holds the letters drawn in the frame. This means that compressed fonts can be used in animations and for scrolling text if the cache is big enough.

If the cache is not big enough, the performance hit of decompressing the letters is not big. This means that compressed fonts can be used for static texts with a (to) small cache without big performance problems.

Configuration

As mentioned in the introduction font compression is enabled by selecting it on the individual Typographies in the Designer. The necessary decompression code is automatically included by the application.

Cache size

The cache size can be configured in the Designer. Select "Text Configuration" in the Config tab:

123

Configuring the cache size.

If the cache is smaller than required by the largest compressed letter the TouchGFX Designer will report an error in the log:

Error output when the cache is not big enough.

Change the font cache to be bigger than the reported number.

The font files shows the minimal cache size required to draw all the letters in the font. Find this information in the TouchGFX/generated/fonts/src/Table_xxx.cpp files:

Verdana size 140 compression size and minimal cache size

We see above that the Verdana font in size 140 requires a cache of minimum 4025 bytes to draw all letters.

Caching failure

If the cache is not large enough to hold the letter being drawn the application will stop. The application calls the function CompressedFontCache::unableToCache(const GlyphNode* glyphNode, int byteSize) which never returns.

This will only happen if you modify the generated files manually.

Limitations

It is not possible to use the same font in both compressed 4 Bpp and uncompressed 4 Bpp.