This post describes a couple of performance improvements introduced in RichEdit 8: 1) a more efficient display tree, and 2) a faster rich-text formatting mechanism. Performance is always of interest, partly to make the user experience more enjoyable (animations should be smooth, not jerky, and who wants to wait for things to happen?) and partly to save battery power. RichEdit 8 also has performance improvements when multiple stories are used, something I’ll describe in a future post. In contrast the DWrite path introduced in RichEdit 8 is slower than the GDI path primarily because it handles default OpenType features. The features improve the typography, but they do take time to process. In fact, OneNote disables this extra processing, favoring speed over typographic elegance. We hope to improve the performance for elegant typography in time.
RichEdit’s display tree for a multiline display consists of an array object called a CArray that contains CLine objects. The CLine object is a recursive structure used to cache display information about the text lines and table rows to be displayed on screen and other output devices. A CLine can represent a single line, or it can point to a CArray of CLine’s for table cells in a table row, or to a CArray of CLines for a math paragraph or for the lines in a table cell. A large file requires many CLine’s so in principle the CLine structure should be as small as possible given constraints of what data is needed for scrolling and displaying the text.
Over the years, various metric variables have been added to the CLine structure in order to support enhanced display capabilities. Specifically Page/TableServices (PTS) needed three extra metrics, ideal layout (device independent, possibly subpixel) required five extra metrics, and OfficeArt (used for PowerPoint, Smart Art, and other clients) required four. Having all of these in the CLine penalizes simpler clients such as WordPad and even clients like OneNote when it doesn’t enable PTS. Accordingly RichEdit 8 stores the extra metrics in a property bag which is empty for simple cases like WordPad and is filled with just the metrics needed for the more elaborate models. For simple plain text, all lines have the same height and the same descent. Removing these values from the CLine would make it even smaller, but this feature isn’t implemented in RichEdit 8. Another improvement would be to bypass measuring lines altogether if line wrapping is disabled. This could be handy for scanning through large plain-text data files.
The second performance improvement speeds up character and paragraph formatting changes and also speeds up updates of the display arrays referred to in the first improvement. To understand this improvement, consider that when a user types characters, the characters are inserted into a text buffer gap. This improves typing performance because the buffer memory only has to be moved when the buffer fills up. The text buffer gap has been a RichEdit feature since Version 1.0. The character and paragraph format arrays (CArray’s of format runs) and the CArray’s of CLine’s also have always had buffer gaps, but up to RichEdit 8.0, their gaps were located at the ends of the CArray’s. So when a character format change is made in the middle of the array as during script itemization and a format run is added or deleted, a block move occurred. Similarly when you delete or type enough in the middle of a document to cause the number of CLine’s to change, a block move was needed. To speed this up, the CArray buffer gap was made moveable so that changes always occur in the gap. For C++ aficionados, the CArray is actually a template class, so that it can be reused for many different kinds of objects, such as the CLine and the format runs.