Part 4: Configuring Font Rendering

Scaleform can render text characters in two ways. First, font glyphs can be rasterized into a texture and later drawn using batches of textured triangles (two per character). Alternatively, font glyphs can be tessellated into triangle meshes and rendered as vector shapes.

For most text in an application, textured triangles should always be used for performance reasons. The glyph textures are generated through either dynamic or static caching. Dynamic glyph caching is more flexible and produces higher quality images, while static caching has the advantage of being computable off-line.

Depending on the target platform and title needs, developers can choose from the following font rendering options:

  1. Use the dynamic cache, allowing for both fast animation and high-quality output. The dynamic cache will use a fixed amount of texture memory and will reduce load time as all glyphs do not need to be rasterized and/or loaded.
  2. Use the static cache, loading pre-generated mip-mapped textures from disk. Developers can use the GFxExport tool to generate packed glyph textures ahead of time, stripping out the font vector data so that it does not need to be loaded.
  3. Use the static cache, auto-generating textures at load time with GFx::FontPackParams. This is similar to the previous method, except textures do not need to be loaded from disk.
  4. Use vector shapes to render text glyphs directly.

Using dynamic cache is recommended on higher end systems such as PC, Xbox 360, PS3 and in many cases Nintendo Wii. The dynamic cache will ensure that load times are minimized and the fonts are rendered with the highest quality possible for the given resolution. The use of dynamic cache is also required if developers plan to utilize system or external font support through either GFx::FontProviderWin32 or GFx::FontProviderFT2.

Using the static cache with textures exported by the GFxExport tool is a good option for CPU and memory constrained platforms such as PSP and PS2, where runtime rasterization of glyphs may be too expensive. Static cache can also be used if developers need to avoid dynamic texture updates in their renderer implementation. However, as we optimize vector data memory use, dynamic cache may become a good option even on the lower performing systems. We recommend that developers experiment with their game data to determine the best solution.

Although Scaleform can be configured to do so, vector shapes are rarely used stand-alone for text rendering. Instead, glyph tessellation is generally applied for large glyphs only, combined with texture based methods for efficient rendering of small text. Developers can refer to the Controlling Vectorization section for more details on how to control or disable this option.

Configuring Glyph Cache

Font rendering in Scaleform is configured though either Render::GlyphCacheConfig or GFx::FontPackParams state objects. By default, glyph cache is automatically created by the Renderer2D object on the render thread and initialized for dynamic glyph caching. The glyph packer is only used for static texture initialization; pack parameters are null by default. With this setup, all of the created movies will automatically use the dynamic font cache, unless they come from GFX files pre-processed to have static textures.

Scaleform 4.0 Upgrade Note

Dynamic glyph cache configuration was changed significantly between Scaleform 3.x and 4.0 versions. While GFx 3.3 relied on the GFxFontCacheManager object maintained by the GFxLoader and configured on the main thread, GFx 4.0 replaced it with the GlyphCacheConfig interface maintained by the Renderer2D and configured on the render thread. See section 5.2 for more details on configuring the cache in Scaleform 4.0 and higher.

The glyph cache system is always used when raster glyphs are rendered from a texture. Internally, the cache manager is responsible for maintaining the text batch vertex arrays, which are created when both static and dynamic texture caching is used. When dynamic caching is enabled, the cache manager is responsible for allocating cache textures, updating them with rasterized characters and keeping textures in sync with the batch vertex data. When only static caching is used, cache manager still needs to maintain text vertex arrays, but it does not need to allocate or update dynamic textures.

The static cache is represented by pre-rasterized bitmap textures with tightly packed glyphs. Static texture rasterization and packing can either be done at load time based on GFx::FontPackParams or off-line with the ‘gfxexport’ tool. In both cases, static textures are associated with fonts. Depending on how it was loaded, an embedded font will or will not have a set of static textures containing its font glyphs. If the font does have static textures, they will always be used for rendering that font’s glyphs; otherwise, the dynamic cache will be used if enabled.

In addition to depending on the type of font textures in use, the rendering method also depends on the glyphs destination pixel size. More formally, the following logic is used:

bool Done = false;

if (Font has Static Textures with Packed Glyphs)
{
    if (GlyphSize <
        FontPackParams.TextureConfig.NominalSize * MaxRasterScale)
    {
        Draw the Glyph as a Texture using Static Cache;
        Done = true;
    }
}
else if (Dynamic Cache is Enabled AND
         GlyphSize < GlyphCacheParams.MaxSlotHeight)
{
    Draw the Glyph as a Texture using Dynamic Cache;
    Done = True;
       }


if (Not Done)
{
    Draw the Glyph as Vector Shape;
}

As outlined above, vector rendering is used if texture caches are not available or if the glyphs are too big to be rendered from a texture. The cache approach is chosen based on availability of packed glyph textures in the font. The details of how both of the approaches can be setup are provided in the following sections.

Using the Dynamic Font Cache

The static cache is efficient when the font and character set is restricted, for example, Basic Latin, Greek, Cyrillic, etc. However, for most Asian languages the static cache may consume far too much system and video memory and result in slow loading. In addition, it may happen when using too many different typefaces; in other words, when the total number of embedded glyphs is big (say, 10000 or more). In these cases it makes sense to use the dynamic cache mechanism. Besides, the dynamic cache provides more capabilities, such as optimization for readability that drastically improves the quality. The dynamic cache is enabled and allocates its buffers by default; to disable this you can call:

renderer->GetGlyphCacheConfig()->SetParams(Render::GlyphCacheParams(0));

In the above call renderer is a Render::Renderer2D object, is maintained on the render thread. This call will set the number of dynamic textures used by glyph cache to zero, effectively disabling it. To avoid default temporary buffer allocation, this call should be done before render HAL is initialized.

The dynamic cache rasterizes glyphs and updates the respective textures on demand when drawing text. It uses a simple LRU (Least Recently Used) cache scheme, but with a smart adaptive glyph packing into textures performed “on the fly”.

You can configure the texture parameters in the following way:

Render::GlyphCacheParams gcparams;
gcparams.TextureWidth   = 1024;
gcparams.TextureHeight  = 1024;
gcparams.MaxNumTextures = 1;
gcparams.MaxSlotHeight  = 48;
gcparams.SlotPadding    = 2;
gcparams.TexUpdWidth    = 256;
gcparams.TexUpdHeight   = 512;

renderer->GetGlyphCacheConfig()->SetParams(gcparams);

The above values are used by default.

TextureWidth, TextureHeight - the size of the caching textures. Both values are rounded to the nearest greater power of two.

MaxNumTextures - the maximal number of textures used for caching.

MaxSlotHeight- the maximal height of the glyphs. The actual pixel glyph height cannot exceed this value. Bigger glyphs are rendered as vector shapes.

SlotPadding - the margin value used to prevent clipping and overlapping of the glyphs. Value 2 is good enough in most practical cases.

TexUpdWidth, TexUpdWidth – the size of the image used to update the texture. Typically 256x512 (128K of system memory) is good enough in all practical cases. It’s possible to reduce it to 256x256 or even 128x128, but in this case the texture update operation will happen more frequently.

Dynamic glyph cache tries to pack the glyphs into the textures as tight as possible. This occurs dynamically, preserving the “Least Recently Used” cache strategy. To understand how the font glyph caching works, consider a very simple memory allocator which is allowed to allocate only 4 KB blocks of memory within a given space of 1MB. The allocator can allocate or de-allocate only these 4 K blocks. Obviously, the allocator is capable to have at most 256 simultaneous allocations. But in most cases, the requested sizes are much less than 4 K. They could be 16 bytes, 100 bytes, 256 bytes, and so on, but no more than 4K. In this situation you try to invent some mechanism to handle smaller memory blocks within those 4K blocks and obtain a bigger capacity of allocations within the given space of 1 MB. But the guaranteed minimum still remains the same – those very 256 simultaneous allocations. A similar mechanism works in the dynamic glyph cache, but in 2-dimensional texture space. In other words, glyph cache guarantees the capacity to store squares of MaxSlotHeight+2*SlotPadding. But it also tries to pack the glyphs much tighter, especially the small ones. In average cases this results in increasing the dynamic font cache capacity 2-5 times (for very small glyphs, increasing the capacity may be in factor of 10s). Typically, 60-80% of the texture space is used for the payload, not depending on the glyph sizes.

The total cache capacity (i.e., the maximum number of different glyphs simultaneously stored in cache) depends on the average glyph size. For the typical game UI we can roughly estimate that the above parameters allow for caching from 500 to 2000 different glyphs. The maximal number of cached glyphs must be enough to handle the visible part of any single text field. In this case, if the number of different glyphs in the visible part of a single text field exceeds the cache capacity, the remaining glyphs are drawn as vector shapes.

As was mentioned before, the dynamic cache allows for extra capabilities and provides support for “Anti-alias for readability” and “Anti-alias for animation” options. These options are the properties of the text field that the Flash designer can select in the text dialog panel. Text optimized for readability looks more sharp and readable. Although it also can be animated, this animation is more expensive because it results in more frequent texture update operations. Besides, the glyphs are automatically snapped to the pixel grid to reduce blurriness with the pixel auto-fitting operation, which means the text lines are also snapped to pixels. Visually it has a jitter effect when animating, especially changing scale. The figure below demonstrates the difference between these options.

When “Anti-alias for readability” is used, the glyph rasterizer performs an automatic fitting procedure (also known as auto-hinting). Scaleform does not use any kind of glyph hints from Flash files or any other font sources. All it requires to render text is glyph outlines. It provides identical visual appearance of text independently of the font source. The Scaleform auto-hinter requires certain information from the font, namely the height of Latin letters with flat top. It is necessary to correctly snap glyphs with overshoots (such as O, G, C, Q, o, g, e, and so on). Typically, fonts do not provide such information, so that, it has to be deduced somehow. For that purpose Scaleform uses Latin letters with the flat top. They are: - any of H, E, F, T, U, V, W, X, Z for capital letters, - any of z, x, v, w, y for lower case letters.

In order to use the auto fitter, the font must contain at least one of the above for capital letters and at least one for lower case. If the font does not contain them, Scaleform will produce a log warning:

“Warning: Font 'Arial': No hinting chars (any of 'HEFTUVWXZ' and 'zxvwy'). Auto-Hinting is disabled.”

If the warning appears, the Flash designer should just embed these letters. Typically it’s enough to add just “Zz” in the “Embedding Characters” dialog form, or embed “Basic Latin”.

Using font compactor – gfxexport

The purpose of the command line gfxexport tool is to pre-process SWF files, generating GFX files which are distributed and loaded into the game. During preprocessing, the tool can strip images out of the SWF file, extracting them into external files. External files can be stored in a number of useful formats, such as DDS and TGA. When –fc option is selected gfxeport also will compress font vecrtor data. This is lossy compression so you may need to experiment with parameters to find best compromise between memory consumption and font quality.

Command Line Option Behavior

-fcEnable font compactor.

-fcl <size> Set nominal glyph size. Small nominal size will result in smaller data size but less accurate glyph. The default value is 256. In most cases nominal size 256 can save about 25% of memory (compared to the nominal size of 1024 that is typically used in Flash) without visible quality degradation. However for really big glyphs you may want to increase nominal size.

-fcm Merge edges for compacted fonts. A Boolean flag that tells, whether or not the FontCompactor should merge the same contours and glyphs. When merging the data can be more compact and save 10-70% of memory, depending on fonts. But if the font contains too many glyphs, the hash table may consume additional memory, 12 (32-bit) or 16 (64-bit) bytes per each unique path, plus 12 (32-bit) or 16 (64-bit) bytes per each unique glyph.

Preprocessing Font Textures – gfxexport

When –fonts option is specified, gfxexport can rasterize and export packed font textures, saving them in a user specified format. When loading the GFX file in Scaleform Player, external textures will be automatically loaded and used as a static cache during text rendering. We do not recommend using exported textures for typical GFx application. However it may be beneficial for low end mobile platforms and in some special cases.

Using gfxexport to generate font textures has the following advantages:

Using pre-generated textures does, however, have disadvantages of lower quality text rendering and potentially longer load times compared to using the dynamic cache. Static text uses mip-maps and thus cannot be hinted; this means that it does not honor the “Anti-alias for readability” setting.

The following command line statement will pre-process ‘test.swf’ into the ‘test.gfx’ file, generating additional texture files for all of the embedded fonts.

gfxexport –fonts –strip_font_shapes test.swf

The –strip_font_shapes option will exclude the embedded font vector data from the resulting gfx file. Although this will save memory, it will make it impossible to fall back to vector rendering for large characters, causing them to degrade in quality when stretched beyond the nominal glyph size of the font texture.

The following table lists the font related options for gfxexport. Options are provided for controlling texture size, nominal glyph size and target file format. Most of these options correspond with glyph packer parameters described in the next section, since glyph packer is used by gfxexport when generating font textures.

Command Line Option - Behavior

-fonts Export font textures. If not specified, font textures will not be generated (allowing for either dynamic cache or packing at load time to be done instead).

-fns <size> Nominal size of texture glyph in pixels; default value of 48 is used if not specified. Nominal size if the maximum size of one glyph in a texture. Smaller characters are rendered at runtime by using tri-linear mip-map filtering.

-fpp <n> Space, in pixels, to leave around the individual glyph image. Default value is 3.

-fts <WxH> The dimensions of the textures that the glyphs get packed into. Default size is 256x256. To specify square texture only one dimension can be specified, e.g.: '-fts 128' is 128x128. '-fts 512x128' specifies rectangle texture.

-fs Force separate textures for each font. By default, fonts share textures.

-strip_font_shapes Do not write font glyph shape data into the resulting GFX file.

-fi <format> Specifies output format for font textures where is one of TGA8 (grayscaled), TGA24 (grayscaled), TGA32 or DDS8. By default, if image format (-i option) is TGA then TGA8 is used for font textures; otherwise DDS A8.

Warning: If you plan to use only packed static fonts or glyph packer in your game, you should disable dynamic glyph cache texture allocation as described in section 5.2. If this is not done, the default texture will still be allocated and will remain unused.

Configuring the Font Glyph Packer

The Font Glyph Packer rasterizes and packs the embedded glyphs when loading the file. Also, it is possible that the fonts are pre-rasterized by the GFxExport. As was mentioned before, the Font Cache Manager can use simultaneously both, dynamic and static mechanisms. For example, it is possible to configure the Font Glyph Packer in such a way that fonts with large character sets will use the dynamic cache, while fonts with Basic Latin only will be pre-rasterized and used statically.

As was described earlier, the general strategy is to use the pre-rasterized static textures when available, otherwise use the dynamic cache if enabled.

The font glyph packer is disabled by default, which means Scaleform will use the dynamic cache for all embedded fonts unless pack parameters are explicitly created and set on the loader. This can be done with the following statement:

Ptr<FontPackParams> packParams = *new FontPackParams();
Loader.SetFontPackParams(packParams);

However, when using GFxExport (and the respective .GFx files) the glyphs can be pre-rasterized. The above call only means “do not pack any glyphs when loading”; if the glyphs are pre-packed with GFxExport they will be used as the static cache.

In cases when the static cache is desirable (small total number of embedded glyphs) it can be configured in the following way.

Loader.GetFontPackParams()->SetUseSeparateTextures(Bool flag);
Loader.GetFontPackParams()->SetGlyphCountLimit(int lim);
Loader.GetFontPackParams()->SetTextureConfig(fontPackConfig);

SetUseSeparateTextures() controls packing. If it’s true, the packer will use separate textures for every font. Otherwise, it tries to pack all the glyphs as compactly as possible. Using separate textures may reduce the number of switches made between textures, and so, the number of draw primitives. But it typically increases the amount of system used and video memory. By default it’s false.

SetGlyphCountLimit() controls the maximal number of glyphs to the packed. By default it’s 0, which means “no limit”. If the total number of embedded glyphs in a font exceeds this limit, the font will not be packed. The parameter makes sense when using Asian languages together with Latin-based or others. If you set this limit to 500, most of the Asian fonts will be cached dynamically (if the dynamic cache is enabled), while using the static textures for fonts with a small number of glyphs.

SetTextureConfig() controls the texturing parameters, which are:

FontPackParams::TextureConfig fontPackConfig;
fontPackConfig.NominalSize   = 48;
fontPackConfig.PadPixels     = 3;
fontPackConfig.TextureWidth  = 1024;
fontPackConfig.TextureHeight = 1024;

The above values are used by default.

NominalSize - the nominal size in pixels of the anti-aliased glyphs stored in the texture. This parameter controls how large the very largest glyphs will be in the texture; most glyphs will be considerably smaller. This is also the parameter that controls the tradeoff between texture RAM usage and the sharpness of large text. Note that it’s called “NominalSize”. Unlike the dynamic cache, where the glyphs are stretched to the glyph slots, the static cache packs the glyphs of different size, using the actual bounding boxes. The NominalSize is exactly the same value as if the text height is set in pixels. It also means the dynamic cache has slightly better “resolution capacity” than the static one.

PadPixels - how much space to leave around the individual glyph image. This should be at least 1. The bigger it is, the smoother the boundaries of minified text will be, but the more texture space is wasted.

TextureWidth, TextureHeight - the dimensions of the textures that the glyphs get packed into. These values must be powers of two.

Several use case scenarios and their meanings are listed below:

  1. Everything is set by default. The dynamic cache is enabled; the Font Glyph Packer is not used. The dynamic cache is used in all cases except for pre-rasterized glyphs textures loaded from preprocessed .GFX files.

  2.     Ptr<FontPackParams> packParams = *new FontPackParams(); 
         Loader.SetFontPackParams(packParams);  
         ...
         renderer->GetGlyphCacheConfig()->SetParams(Render::GlyphCacheParams(0));
    
    

    The font glyph packer is always used in all cases. The dynamic cache is disabled.

  3.     Ptr<FontPackParams> packParams = *new FontPackParams();
        Loader.SetFontPackParams(packParams);
        Loader.GetFontPackParams()->SetGlyphCountLimit(500);

The Font Glyph Packer and the dynamic cache both are used. Here fonts with 500 embedded glyphs or less use pre-rasterized static textures, the other ones use the dynamic cache.

Also, it is necessary to mention that several of these capabilities are available only with the dynamic cache. For example, text optimized for readability, with automatic pixel grid fitting (so called auto-hinting) works only with the dynamic cache. Any effects, such as blur, shadows and glow are also available with the dynamic cache only. In general, the glyph packer and static cache are good for low budget systems with poor performance.

Controlling Vectorization

As described earlier in this document, tessellated vector shapes are used in Scaleform as a fallback when rendering large glyphs that do not fit into the texture cache. Since there is no limit on the size of individual text characters in Flash, the amount of memory required for rendering them through textures becomes prohibitive beyond a certain point. One way to address this problem is to limit glyph bitmap size and use bilinear filtering from that point on. Unfortunately, doing so will cause rendering quality to quickly degrade.

To allow rendering large high-quality text, Scaleform is capable of switching glyph rendering to triangulated shapes and then leveraging the edge anti-aliasing technology. In most cases, the switch will not be noticeable by the user, as glyph shapes will retain their smooth edges when text characters grow in size. The following figure demonstrates the difference between texture and triangle mesh rendered text. In Scaleform Player, the executable Ctrl+W key toggles the wire-frame mode, allowing users to see how the rendering is done.

Although triangle-rendered glyphs look good, they require more processing time during rendering due to the increased triangle and draw primitive counts. When the dynamic cache is used the maximum texture glyph height is determined by the MaxSlotHeight value of the cache, modifiable through Render::GlyphCacheConfig::SetParams. If the glyph to be displayed fits within the texture slot it is rendered through a texture, otherwise vector rendering is used. Since the rendering technique used is determined per glyph, a single line of text can contain both bitmap and vector symbols, when its pixel height is close to the maximum cache slot height. Due to sub-pixel precision, the differently rendered symbol types are almost indistinguishable from each other.

The static cache works differently. In a static cache all glyphs are pre-rasterized based on the specified nominal font size and scaled using a tri-linear filter. Since the nominal font size is fixed, the text rendering approach selection is done based on the nominal font size and not the height of the individual glyphs.

For both dynamic and static cache, developers can control the point at which the texture to vector rendering switch occurs by modifying the GlyphCacheParams:: MaxRasterScale value. MaxRasterScale assigns the multiplier of maximum texture slot size, in pixels, after which the switch to vectors will occur. The default value for MaxRasterScale is 1.0f. The value of 1.25, for example, would mean that the player will render text using textures unless the on-screen glyph size becomes greater than 1.25 times the nominal glyph size stored in textures. With the default nominal size of 48 pixels, vector rendering will be used for glyphs bigger than 60 pixels on screen.

If the MaxRasterScale value is set high enough vector rendering will never be used. Vector rendering is also disabled for GFX files produced by executing the gfxexport tool with the -strip_font_shapes option. In the later case, font glyph data no longer exists in the file so vectorization is not possible.