Skip to main content

Flash-limited GUI development

This article explains how to use TouchGFX to develop graphical user interfaces with low flash memory usage.

When generating code with TouchGFX, assets like images, texts and fonts are converted to C++ files, which are then stored in flash memory during programming along with TouchGFX application code, user code and TouchGFX libraries. This means that large or complex projects with many assets will result in high flash usage.

Since memory resources are limited in most applications, there are ways to decrease the amount of flash memory needed. TouchGFX has four built-in features, which can help you decrease the flash memory needed for your application significantly. The four concepts are L8 image format, image compression, scalable vector graphics (SVG) and vector fonts.

In this article we will now see how to utilize the four concepts to limit the flash memory footprint of an application. The measurements were performed using an STM32U5G9J-DK2, but the concepts to save flash can be applied to other hardware platforms as well.

Further reading

Read the Memory Usage article for more information on memory management.

Flash-limited E-bike demo

A flash-limited E-bike demo, which demonstrates how to use the four features to save flash with TouchGFX can be found in TouchGFX Designer. The demo is a board specific demo for STM32U5G9J-DK2. A benefit from using the STM32U5G9J-DK2 is that it has NeoChromVG GPU, which hardware accelerates vector rendering. However, especially L8 image format and image compression can be used across all STM32 MCUs.

If the demo was implemented with only uncompressed bitmaps (without L8, compression, SVG and vector fonts), it would take up approximately 10.5 MB of flash memory. In the implementation, where the four concepts to save flash are used, the demo only takes up approximately 800 KB of flash memory. As seen from this, it is possible to save a substantial amount of flash. In the demo, the four concepts combined gives a flash saving of 92%.

When to use which concept

Technical information and guides on how to use the four flash saving concepts with TouchGFX can be found here:

The four concepts can all help to decrease the amount of flash memory needed for an application. When to use which concept depends on the scenario, but some general rules can be applied. We will now go through these.

L8 and compression

When using L8 image format on a bitmap, the bitmap can have a maximum of 256 colors. The rendering of assets in L8 format is hardware accelerated with Chrom-ART, meaning that the render time for L8 bitmaps are almost the same as for regular bitmaps. Sometimes it is even faster to render a bitmap in L8 format compared to a regular bitmap, since less data is read from the flash memory. L8 can save you more than 70% of flash compared to a regular bitmap with a color depth of 32-bits.

Image compression can help you to save even more flash compared to L8. Image compression comes in two formats: L8 compression and RGB compression. To use L8 compression on a bitmap, the bitmap needs to be in L8 format first, since the L8 compression algorithms use the color look-up table. As a result of this, L8 compressed bitmaps are still limited to a maximum of 256 colors. Some of the L8 compression algorithms have further limitations on the maximum number of colors. L4 is limited to a maximum of 16 colors and L8 RLE is limited to a maximum of 64 colors. RGB compression doesn't require the bitmap to be in L8 format and can hence be used on all bitmaps. Opposed to rendering of L8 bitmaps, which are hardware accelerated with Chrom-ART, compressed bitmaps (both L8- and RGB compressed) are software rendered and are hence more expensive to render. One further limitation of compressed bitmaps is that they can't be used in scalable or rotatable widgets.

Both L8 image format and image compression are easy to use, if you already have your assets in PNG-format. In general you could always use L8 image format on bitmaps with maximum 256 colors without any significant performance penalty. If you need to save even more flash and have available render time to achieve the required performance, you could use L8 image compression as well. If you want to compress bitmaps that are not compatible with L8 format, you can use RGB compression instead. If you need to use your bitmap in scalable or rotatable widgets, you can't use compressed bitmaps natively. There are of course ways around this, which we will briefly cover later.

SVG

Another option to save flash on assets is to have them in SVG-format instead of PNG-format. SVGs has no limitation on the number of colors. However, SVGs are usually more costly to render compared to L8 and compression. Therefore, it is recommended to limit the number of concurrent SVGs and to keep animations with SVGs to a minimum. The simpler your SVG assets are, the smaller the resulting flash footprint will be and the better performance can be achieved. In line with this, it is recommended to only use one-layer SVGs.

To use an asset in an SVG widget, you need to import the asset in SVG-format. The assets can't be converted by TouchGFX.

Vector Fonts

As for non-font assets in SVG-format, Vector Fonts are also expensive to render, since it is rendered in the same way as SVGs. Vector Fonts are generally beneficial to use for large font sizes or if the same font is used in several different sizes. If you have the same font in different sizes and vector representation, it is only needed to store the font once. Then, only a scaling factor is needed to represent it in different sizes. For bitmap fonts each of the font sizes needs to be stored separately. Vector Fonts will often not yield as big a flash saving as image compression and SVG. Therefore, it should mainly be used, if it is necessary to limit the flash memory footprint to the absolute minimum.

Examples

To provide specific examples of the characteristics of the different concepts, measurements are performed on two assets from the E-bike demo in regular bitmap, L8, L8 RLE compressed, RGB compressed and SVG-format. The measurements are performed on the STM32U5G9J-DK2 with assets in internal flash. The render time of SVG will be longer for MCUs not having NeoChrom GPU. However, a faster CPU could also compensate for this.

The asset below has a color depth of 32-bits and is a button with an icon on top. The button is 122 x 112 px and the icon is 72 x 72 px.

Button with icon from E-bike demo

The following measurements are performed on the asset:

FormatSizePct.Render timeCPU load
Bitmap:75.4 KB100%0.414 ms2.4%
L8:19.3 KB25.6%0.448 ms2.3%
L8 RLE:2.55 KB3.4%1.51 ms9.6%
RGB:4.53 KB12.0%1.65 ms10.5%
SVG:3.01 KB4.0%1.43 ms4.1%

As seen from this, L8 RLE has the lowest flash footprint, but takes approximately 1 ms longer to render compared to regular bitmap format.

The asset below has a color depth of 32-bits and is a part of the gauge in the dashboard screen in the demo. It is 150 x 436 px.

Part of gauge from E-bike demo

The following measurements are performed on the asset:

FormatSizePct.Render timeCPU load
Bitmap:261.6 KB100%1.15 ms1.5%
L8:65.6 KB25.1%1.24 ms1.4%
L8 RLE:4.66 KB1.78%2.75 ms15.0%
RGB:10.5 KB4.01%3.08 ms17.6%
SVG:0.686 KB0.27%3.40 ms2.0%

The asset in SVG-format has the by far lowest flash footprint, but is also the most expensive to render.

Conclusion

As seen above, none of the formats are best for all assets. However, for flash saving alone, L8 image compression generally has the smallest flash footprint when the asset is relatively small, and SVG has the smallest flash footprint when the asset is larger. Even though SVG assets can have more than 256 colors, it is still recommended to keep SVG assets relatively simple for both flash memory usage and performance considerations. L8 image format has the highest flash footprint among L8, image compression and SVG, but L8 has the lowest render time.

Therefore, when developing a flash-limited application, you need to identify the bottleneck and determine what matters most for you application. Is it performance or flash memory savings? If it is performance, L8 will be the most efficient approach. It could even be combined with compression for some assets if performance allows it. If it is most important to have the smallest possible flash footprint, image compression and SVG will be the best approach.

Using ''smart'' assets

Both flash savings and performance can be improved by using ''smart'' assets. Here, smart assets both refer to the assets themselves and their usage.

Simple assets

First of all, the simpler the assets are, the more they can be compressed and the simpler the SVG definitions will be as well. This will in the end result in a smaller flash footprint.

As a reference, in the E-bike demo all bitmap assets have a maximum of 64 colors and since the assets are also well-suited for L8 RLE, all the bitmap assets are L8 RLE compressed. Since L8 RLE is faster to render compared to L8 LZW9, this also gives the best performance.

Additionally, if the design is also simple, you can in some cases use a Tiled Image as well. Then you only need to store a part of the image, which can then be repeated.

Using boxes

Another way of saving flash memory in a smart way is to use the Box widget. Using boxes has two main benefits:

  1. Firstly, the box will be drawn directly in the framebuffer. As a result of this, no flash memory is needed to store the box. The only flash impact will be from the small amount of code needed to define the box.
  2. Secondly, boxes are adaptable and it is possible to change both color and size at runtime. In the flash-limited E-bike demo, boxes have been used for e.g. solid background colors to support both light- and dark mode by changing the color of the box.

Reusable assets

Another way to save flash memory is to limit the total number of assets. This can of course be achieved by having a very simple design, but it is sometimes also possible to limit the existing number of assets by reusing the same asset multiple times.

In the flash-limited E-bike demo, this approach is, for example, used in the weather screens. The weather icons are the same asset that is scaled to fit all 3 sizes. Since it is a scaled asset, SVG format is preferred over compression even though the flash footprint is smaller with RLE. However, the total flash footprint would be bigger if the asset was stored in RLE format in all 3 sizes.

Additionally, the buttons are also implemented as a reusable asset. For example, the buttons are separated from their icons to allow it to be reused.

Another case where reusability is used in the demo is with the background. There are a total of 5 different background designs in the demo, but by using adaptable assets (boxes and reuseable assets), all the backgrounds are created with just one asset and a box.

Improving performance

When developing a flash-limited application there is a risk of hurting performance. However, if spare RAM is available, there are ways to improve the performance of the application. By using Dynamic Bitmaps and Cacheable Containers, it is possible to draw static snapshots of assets or containers to RAM. Then, whenever using the asset or container, the cached version can be used instead. The cached version is just a bitmap in RAM, which means that the render time is identical to rendering regular bitmaps.

In the flash-limited E-bike demo, cachable containers are used to allow scrolling of SVG assets and vector fonts, while keeping a good performance.

Weather element as cacheable container

In general vector fonts are cached whenever they are moved or movable on runtime. This ensures a minimum of 30 FPS despite scrolling vector fonts.

Another trick used in the demo is decompression of compressed bitmaps to the bitmap cache. By decompressing the bitmaps to RAM, it is possible to use them in scalable and rotatable widgets. When a bitmap is decompressed, TouchGFX will always use the decompressed cached version when referring to the bitmap ID. An example from the E-bike demo is seen below. The compressed bitmaps are simply decompressed and after this the bitmaps can be used just as regular bitmaps.

StartView.cpp
StartView::StartView()
{
Bitmap::decompress(BITMAP_MAIN_RIPPLE_LEFT_ID); // Decompress compressed image to bitmap cache
Bitmap::decompress(BITMAP_MAIN_RIPPLE_RIGHT_ID); // Decompress compressed image to bitmap cache
}

void StartView::setupScreen()
{
leftMainRippleScale.setBitmap(BITMAP_MAIN_RIPPLE_LEFT_ID); // Set bitmap for Scalable Image
rightMainRippleScale.setBitmap(BITMAP_MAIN_RIPPLE_RIGHT_ID); // Set bitmap for Scalable Image
leftMainRippleScale.setWidthHeight(0, 0); // Set scale of image
rightMainRippleScale.setWidthHeight(0, 0); // Set scale of image
}

As a result of extensive use of dynamic bitmaps, the flash-limited E-bike demo requires a bitmap cache of 922 KB. This means that a lot of spare RAM is required. However, the demo also takes the flash saving to a quite extreme extent. In a more simple application or an application where performance requirements isn't that important, it would be possible to get away with a much smaller bitmap cache.

Conclusion

In this article we saw how to limit the flash usage of a TouchGFX application.

The 4 flash saving concepts can be used across all STM32 hardware platforms, but keep in mind that SVGs and vector fonts are expensive to render. As a result of this, performance can decrease if no hardware acceleration of vector rendering or extra computing power is available.

The concepts for flash-limited GUI development can result in a performance penalty, but it is possible to improve the performance by using dynamic bitmaps and cacheable containers.

By using the flash saving concepts there is a huge potential for saving flash. The exact amount of flash, the concepts can save, will depend on the specific application, but for the flash-limited E-bike demo, a flash saving of 92% is achieved.