Spire
12-01-2012 00:08:32
For the project I'm currently working on, which uses MyGUI as its UI engine, I've made some fairly extensive modifications to some of the MyGUI font-related code to improve performance and to fix bugs. I would like to contribute these modifications to MyGUI if the MyGUI developers are interested. Now that MyGUI 3.2.0 appears to be nearing official release, it seems like perfect timing to have these modifications considered for inclusion in this release. I have them available in the form of a patch against the latest revision of MyGUI in the repository.
Here's what my patch does:
Here's what my patch does:
- Dramatically reduces the amount of texture memory used for a TrueType font. It achieves this by doing two things:
- Eliminate duplicate glyphs from the font texture in cases where multiple code points use the same glyph. The most common case where this occurs is with the "not defined" glyph.
[/*:m]
- Pack glyphs onto the font texture as tightly as possible by first getting rid of all bearing- and advance-related whitespace, reducing each glyph to its absolute minimum "printable" bounding box. Then arrange all the glyphs by height within the font texture.[/*:m][/list:o]
Some real-world examples:
- Take the "DejaVu Sans" font that is bundled with MyGUI. Instantiating this font at 7 pt (96 PPI) with all supported characters previously resulted in a 1,024×1,024-pixel texture occupying 2 MiB of texture memory; now the exact same font is contained in a 512×512-pixel texture occupying 512 KiB -- a 75% savings.
Here are "before" and "after" versions of this font texture to illustrate the difference (click for full size):
[attachment=1]DejaVu Sans 7 pt (96 PPI) - Before.png[/attachment]
[attachment=0]DejaVu Sans 7 pt (96 PPI) - After.png[/attachment]
[/*:m]
- Instantiating the same "DejaVu Sans" font at 30 pt (96 PPI) with all supported characters previously resulted in a 4,096×4,096-pixel texture occupying 32 MiB of texture memory; now the exact same font is contained in a 2,048×2,048-pixel texture occupying 8 MiB.
[/*:m]
- Finally, take the "MingLiU" font that comes with Windows 7. It's a monster 30 MiB font containing many thousands of glyphs. Instantiating this font at 14 pt (96 PPI) with all supported characters previously resulted in a 8,192×4,096-pixel texture occupying 64 MiB of texture memory; now the exact same font is contained in a 4,096×4,096-pixel texture occupying 32 MiB.[/*:m][/list:u]
As you can see, this improvement lowers the GPU system requirements for any MyGUI application that uses fonts. It also makes it possible to use fonts and font sizes that were previously impossible due to running out of texture memory.
[/*:m]
- Improves the performance of ResourceTrueTypeFont::initialise() by using internal template classes and functions to perform compile-time branching. Previously the code was branching on invariants such as mAntialiasColour and rgbaMode many thousands of times inside tight loops. Now all of this branching is done once -- and only once -- at the very start of initialization. This significantly decreases application startup time, especially when large fonts are in use. Using the above example of the MingLiU font at 14 pt (96 PPI), initialization time has been reduced from 1.3 seconds to 0.8 seconds (running on my Core-i7-2600K-based development machine in Release mode).
[/*:m]
- Improves the runtime performance of ResourceTrueTypeFont::getGlyphInfo() by using a single consolidated code-to-glyph map instead of multiple vectors of included and excluded ranges of code points. This makes glyph lookup an O(log n) operation instead of O(n) (or in some cases, even worse). Because ResourceTrueTypeFont::getGlyphInfo() is called all the time during text layout and rendering, this makes MyGUI applications run faster.
[/*:m]
- Adds support for TrueType fonts that contain embedded "SBIT" bitmaps. Many TrueType fonts include such pre-rendered bitmaps for use at specific sizes; these bitmaps are then used instead of the usual splines. Previously any embedded bitmaps would be rendered as unreadable garbage, which was an unwelcome surprise if you happened to choose a font size that used bitmaps; now they are always rendered correctly.
[/*:m]
- Fixes line-spacing calculations. Instead of arbitrarily basing the line spacing on the tallest glyph that is currently in use, the code now uses the metrics directly from the font, which are set by the font designer. Not only is this more typographically correct and more aesthetically pleasing, it is also consistent with other applications. Perhaps more importantly, it prevents the layout of the application from shifting around unpredictably when glyphs are added to or removed from a font definition.
[/*:m]
- Fixes glyph bounding-box calculations. Rendered glyphs are now guaranteed to never overlap inside the font texture under any circumstances; consequently, the "Distance" property of the "ResourceTrueTypeFont", which was cumbersome and error-prone to use, is now deprecated and ignored. A warning is issued to the MyGUI log when this property is used.
[/*:m]
- Fixes handling of whitespace characters. Previously the ResourceTrueTypeFont code would reject any glyph that FreeType delivered with a null bitmap, even though this is normal and correct behavior for any character whose glyph is pure whitespace, such as "Space" (32), "Non-Breaking Space" (160), and many more.
The ResourceTrueTypeFont code previously provided special handling specifically for the "Space" character in order to work around this problem, but this required that the user manually specify the width of that character using the "SpaceWidth" property. Even with this workaround in place, however, all other whitespace characters would still be broken and could not be used at all.
Now all whitespace characters are handled correctly, so the "SpaceWidth" property is no longer needed. For backward-compatibility reasons it can still be used, but it is now deprecated with a warning message. Omitting this property causes the width of the "Space" character to be automatically calculated using typographically correct values directly from the glyph metrics.
[/*:m]
- Uses the standard "Not Defined" glyph as a substitute for code points that are not defined in the current font. (This glyph usually looks like a square.) Previously the code would use a "Space" glyph, which is not only non-standard but also provides no obvious indication to the user or to application developer that a glyph is actually missing.
[/*:m]
- Fixes problems with the handling of glyph bearing and horizontal width. Previously, sequences of consecutive overlapping characters, such as "fj", would be rendered incorrectly with the overhanging portions of one or more glyphs clipped by neighboring glyphs. Now glyphs always correctly overlap. Related problems in calculations involving cursor positioning and word wrap have also been corrected accordingly.
[/*:m]
- Implements subpixel positioning. This significantly improves the overall apperance and "color" of text, especially at small sizes and in even more so in pathological cases where a widget has overridden a font's height by setting the "FontHeight" property to a low value. For example, in cases where spacing in between glyphs is supposed to be around 1.5 pixels, the code would previously sometimes use 1 pixel and sometimes use 2, resulting in very uneven spacing. This problem no longer occurs at all, as some portions of horizontal text layout are now performed internally using floating point.
Note that the use of floating point for some calculations does not appreciably hinder application performance. In fact, extensive benchmarking has shown that the overall performance of the font layout and rendering code is still much higher than before.
[/*:m]
- Provides reasonable defaults for several properties of "ResourceTrueTypeFont". For example, when omitted, "Resolution" now defaults to "96", which is the de facto standard default screen resolution used almost everywhere. Also, "TabWidth", when omitted, defaults to the width of eight "Space" characters. These changes make font definitions simpler and cleaner to read and write.
[/*:m]
- Makes code ranges much simpler to specify. Previously the application developer would have to painstakingly specify which ranges of code points to include and which ones to exclude. This was of vital importance because failing to exclude undefined ranges of code points could cause the font texture to be filled up with unused garbage and become huge; however, there was no easy way to determine which ranges needed to be excluded in this way.
Although it is still possible to include and exclude code ranges, a developer who simply wants to include everything that the font provides can now simply specify a single range of "0 65535" with no exclusions. The code is smart enough to automatically skip over undefined glyphs and not any waste space in the resulting font texture.
[/*:m]
- Miscellaneous code cleanup. This patch eliminates unnecessary duplicated code in several key areas by moving it into functions and function templates. This process of doing so has also uncovered some bugs involving inconsistencies between duplicated sections of code that should have been the same. Those bugs are now fixed, and the code is now easier to maintain now that the logic occurs at a single point rather than being copy-pasted in several places. Some sporadic crashes caused by insufficient error checking and incorrect calculations have also been fixed.[/*:m][/list:o]
I think that about covers it -- thanks for reading this far! Please note that although these changes are extensive, they have been heavily tested, used, and refined for quite a while now by both me and my development team. I believe that they are of high enough quality to be released and contributed to the official library. In anticipation of this, I've tried to conform to MyGUI's coding and style conventions as closely as possible. Please let me know if you are interested in evaluating this patch for the purposes of possible inclusion in MyGUI 3.2.0.
- Take the "DejaVu Sans" font that is bundled with MyGUI. Instantiating this font at 7 pt (96 PPI) with all supported characters previously resulted in a 1,024×1,024-pixel texture occupying 2 MiB of texture memory; now the exact same font is contained in a 512×512-pixel texture occupying 512 KiB -- a 75% savings.
- Eliminate duplicate glyphs from the font texture in cases where multiple code points use the same glyph. The most common case where this occurs is with the "not defined" glyph.