Invalidation/Redraw -- A Graphical Interface Pattern

Category: Graphical Interface

Intent

Used to optimize redraws in a graphical system where there tend to be a large number of visual changes, each involving only a small part of the graphic.

Motivation

A naive implementation of a graphical system redraws an entire graphic immediately every time any part of that graphic changes. This is acceptable for some purposes, but becomes unacceptably in situations like graphical user interfaces, where it can result in large numbers of redraws and poor performance. For instance, suppose the user drags a window in front of another complex window. To redraw the back window for every pixel of mouse movement is unnecessary and extremely slow. Instead, it is better to store a series of events indicating which parts of the back window were blocked, and redraw the window only once after all the user releases the front window. If greater update responsiveness is called for, the back window can be redrawn at regular intervals, at each interval redrawing all changes since the previous redraw.

Applicability

Invalidation/Redraw is useful in graphical systems where If changes are infrequent, Invalidation/Redraw complicates the system with little performance gain. If instantaneous updates are required, Invalidation/Redraw may be too slow due to the delay between the redraw request and the redraw.

Consequences

Invalidation/Redraw introduces some additional memory usage to store the collected union of the update request regions.

In systems where updates are frequent, drawing time is significantly reduced. The amount of reduction depends on the time between redraws-- more frequent redraws require more time but improve responsiveness.

Implementation

Consider the following issues when implementing the Invalidation/Redraw pattern:
  1. Invalidation/Redraw may be most easily implemented using rectangular regions for invalidation. However, providing more general geometric shapes should improve performance, as it will limit the number of pixels which will actually have to be drawn in some cases. For instance, a circular dial object can be invalidated as a circle, rather than a rectangle, eliminating the need to redraw the pixels at the rectangle corners.
  2. Generally a system which supports "rectangles only" for individual invalidation events will support an arbitrary union of rectangles as the accumulated invalid area-- this is easily implemented by storing a list of invalid rectangles internally and iterating over them at redraw time. However, an even simpler approach is to use a single rectangle as the collected invalid region; new rectangles are added by expanding the invalid rectangle to cover them. This may result in drawing a much larger area than necessary, but in some applications (like user interfaces) where updates tend to be localized, it may be a reasonably approximation.
  3. Support for general shapes also allows all invalid regions to be unified into a single region, which is then redrawn in a single step. This approach significantly reduces the number of redraws required and is usually faster than the alternative (redrawing each invalid region separately), but can also significantly increase the complexity of the system, especially if there is no low-level support for general geometric regions.
  4. On systems supporting multithreading, it may be possible to perform the redraws in a separate thread. In many existing systems, however, the redraws are performed by calling a Redraw procedure from the main event loop.
  5. Shorter time intervals between redraw cycles will require more time, but will improve apparent responsiveness. However, intervals too small can require so much drawing time that the entire program slows down. In user interface applications, this is generally not a problem, because invalidate events tend to happen in small clumps. But it could become a problem in an application which generates long and steady streams of invalidate events.
  6. It is often possible to improve performance by doing some simple optimizations. For instance, if any of the requested invalid regions is entirely enclosed by any other, the inner one can be discarded. Also, if there is a clipping region associated with drawing, any invalid regions outside the clipping region can be discarded.

Known Uses

Invalidation/Redraw is found in most graphical user interface packages. Examples include ET++, the Think Class Library, VisualWorks Smalltalk, and the MacOS Toolbox. The pattern is well suited to some kinds of animation packages, but I know of no examples of its use for such applications. Please send any additional examples to Greg Ferrar.

Back to the ET++ pages