Introducing the New Avalon Graphics Model

 

Ian Griffiths
DevelopMentor

February 2004

Summary: Introduces the new Avalon rendering and composition technology, and provides details about the new programming model that allows user interfaces to be defined declaratively. (16 printed pages)

Contents

Repainting vs. Markup
Vector Graphics in Avalon
XAML Drawing Primitives
Brushes
Transformations
Other Goodies
Conclusion

Before "Longhorn," the techniques used by Microsoft Windows applications for graphics handling had remained essentially unchanged since Windows was introduced. There have been many incremental improvements over the years, of course, so the functionality available today through GDI+ is orders of magnitude more powerful than anything offered by early versions of Windows. However, the basic nature of the way the operating system (OS) and the application cooperate to maintain an application's appearance is older even than Windows itself, decades old in fact. While this venerable approach to graphics has served us well, it is showing its age.

Longhorn introduces "Avalon," a technology that brings two significant advances to the way user interfaces are built. First, it provides a new rendering and composition model that enables you to use much more advanced display techniques in your applications, taking advantage of the capabilities of modern graphics cards. Second, there is a new programming model that allows user interfaces to be defined declaratively. In this article I explain what these changes mean for application developers and I illustrate some of Avalon's new functionality.

Repainting vs. Markup

Traditionally, Windows has used a reactive repainting model for managing an application's appearance: applications have been required to provide code that can repaint any part of a window from scratch on demand. The application typically draws onto the screen, which means that if any part of the window later becomes obscured by some other window, the OS has no record of what was there. If the window later becomes visible again, Windows has to ask the application to draw the window again, as Figure 1 shows.

Aa480164.avalongraphics01(en-us,MSDN.10).gif

Figure 1. The Windows redraw model

Different languages and libraries each have their own ways of representing these repaint requests to developers. For example, Windows Forms has the Control.Paint event and associated OnPaint method. Visual Basic 6.0 has its Paint event. MFC has methods such as OnDraw. However, all of these ultimately rely on the same mechanism: repaint requests delivered by the OS to the application via the Win32 WM_PAINT message.

This repainting approach is flexible, in that the application can be in complete control of its appearance. It is also reasonably efficient in its use of memory, as the OS does not need to remember what is in each and every window. The only image that needs to be stored is the current contents of the screen, a job that is done by the graphics card. Any area of any window not currently visible (either because the window is obscured by another window, or because it is minimized) does not need to be stored at all.

However, this memory efficiency is not as important as it once was—the typical memory capacity of the average PC has increased substantially since the days of Windows 1.0. Of course, display resolutions have gone up too, as have color depths, which means the amount of memory required to store the contents of a window will also typically be higher. However, screen sizes have not increased as fast as memory capacity, so on a modern graphics card, only a small portion of the video memory is used for the screen buffer. The rest is typically used for texture maps when playing games or using DirectX-based applications, but is rather underused the rest of the time.

Since memory is no longer the scarce resource it was when Windows 1.0 came out, other graphics handling models look more attractive than they once did. One of the simplest alternative models is for the OS to store the appearance of a window in a bitmap, so that the application only needs to draw it once. (This is sometimes called a "backing store" approach.) In fact, Windows has been able to support this approach for some time: An application can get backing store as a side-effect of using the "layered" window functionality added to Windows 2000.

Moving away from the traditional repainting approach makes much easier certain composition effects that were previously hard to achieve. For example, it is possible to make a layered window partially transparent, so that the windows behind it show through. This allows windows to fade in and out of view by gradually changing their transparency. (Microsoft Outlook 2003 uses this technique to make pop-up e-mail alerts less obtrusive, for example.) While it would be technically possible to do this with a repainting approach, it would mean that both the transparent window and all the windows visible through it would need to be repainted every time any one of the windows moved or changed. This would be desperately slow. But as Figure 2 shows, with the layered technique, a window's contents can be stored in some spare video memory, and then the graphics card can create the semi-transparent composition of these windows. Since most cards have special-purpose hardware for doing composition, it can work very quickly, placing only a minimal load on the CPU.

Aa480164.avalongraphics02(en-us,MSDN.10).gif

Figure 2. Layered windows

Moving away from the repainting approach also allows an application to use a simpler technique for drawing if it wants to—it can just treat the window as a canvas on which to draw images, safe in the knowledge that they will stay there. As Figure 2 shows, Windows does not need to send WM_PAINT requests to the application, because it stores the complete contents of the layered window. This can simplify drawing handling for some kinds of applications. (Visual Basic 6.0 offered this style of drawing without using layered windows. However, this facility was provided by the Visual Basic runtime, which still had to support normal Windows repainting on behalf of the application.)

The bitmap approach used by layered windows is not the only way of providing a more stable drawing model. Consider an HTML-based user interface. The application writer merely needs to provide the markup that describes their user interface. This has the same benefits that layered windows offer: the application can simply generate a particular page's contents once. There is no need for the application to be able to repaint the display simply because windows have been moved or resized—as Figure 3 shows, the HTML control retains the structure of the markup, which enables it to handle the repaint requests for the application. It is only when the user navigates to a different page that new markup will have to be supplied.

Aa480164.avalongraphics03(en-us,MSDN.10).gif

Figure 3. Markup-based drawing

A particularly interesting aspect of HTML is the DOM (Document Object Model). While a bitmap backing store simply retains an image of how the window should look, an HTML browser stores each of the text blocks and other elements used to make up the display. This makes it much easier for an application to modify the UI. With a bitmap, the only way to modify the existing image is to paint over what was there before, but with a DOM, the structure of the UI remains in a form that the program can access and modify at runtime, making it possible to go back and alter a part of the UI that you "drew" earlier. One problem with the HTML DOM, however, is that its set of primitives does not allow us to build particularly visually rich applications—HTML user interfaces tend to have to fall back on the use of bitmaps if they want to look good.

The Avalon graphics model offers the best features of all three of these approaches. As with HTML, Longhorn user interfaces can be built using markup, and at runtime the structure of this markup is available through an object model. (It is, of course, a .NET object model, rather than the HTML DOM.) However, unlike HTML, this technique has all the flexibility that would be available to classic Win32 or Windows Forms applications under the old repainting model. This is because the markup language in Longhorn is much richer than HTML—there is a section of the markup language devoted to graphics primitives, and it offers even richer functionality than GDI+. Furthermore, Longhorn offers a powerful composition model for combining multiple user interface elements. Where layered windows only supported composition of top-level windows, Longhorn offers much finer-grained composition. Each element in the UI, down to the individual graphical primitives, can have its own transparency settings. This means that for the first time, Windows has a user interface hierarchy that fully supports partially transparent controls.

Vector Graphics in Avalon

The MSAvalon.Windows.Shapes namespace defines a number of element types that allow pictures and drawings to be described in markup alongside controls, text, and other elements. This endows XAML with vector drawing capabilities similar to SVG (Scalable Vector Graphics), a dialect of XML defined by the W3C for describing vector graphics.

A frequently asked question is: Why doesn't Avalon just use SVG? On the face of it, it seems strange to invent a new way of representing vector graphics in markup when a standard already exists. However, the principal advantage of these shape classes is that they have been designed to integrate into the Avalon programming model. The vector drawing elements derive from the same FrameworkElement base class as all other XAML elements, and follow the same naming conventions. SVG has its own set of conventions for element and attribute names that is at odds with the existing .NET Framework class library. Furthermore, SVG elements were not designed to fit into the Avalon object model. By not using SVG, Avalon ensures that vector graphics can be mixed in with any other kind of markup in your XAML, and can take advantage of all of the same layout facilities. (Note that in the version of the Longhorn SDK documentation released at the 2003 PDC, the XAML elements used for vector drawing are sometimes referred to collectively as WVG (Windows Vector Graphics). However, Microsoft is no longer using this name, because it implies, incorrectly, that these elements are somehow distinct from all of the other elements supported by Avalon.)

The vector graphics elements are fairly straightforward to use. They let you describe pictures as a collection of drawing primitives. The following example shows a drawing made up of a rectangle and a circle. (There is no circle primitive, we just use the Ellipse element, making the two radii the same.)

<Canvas xmlns="https://schemas.microsoft.com/2003/xaml/">

  <Ellipse CenterX="80" CenterY="80" RadiusX="50" RadiusY="50"
           Fill="PaleGreen" Stroke="DarkBlue" StrokeThickness="5"/>

  <Rectangle RectangleLeft="65" RectangleTop="0"
             RectangleWidth="30" RectangleHeight="160" 
             Fill="Yellow" Stroke="Black" StrokeThickness="3"/>

</Canvas>

The results are shown in Figure 4. Here we have used a normal Canvas as the container for these shapes, but any kind of container can be used—shapes can be placed almost anywhere in the markup just like any other element.

Aa480164.avalongraphics04(en-us,MSDN.10).gif

Figure 4. Simple shapes in XAML

The ability to represent drawings in markup is important, as it provides a persistent way to represent scalable drawings without the limitations that bitmaps suffer from. While it is possible to store any image as a bitmap, they only look right when displayed at a certain size. Bitmaps are really nothing more than a collection of pixels to be displayed on screen, which causes problems if you try to display them at anything other than their natural size. If you enlarge them, you hit the problem that you are trying to fill more pixels on screen than there are in the bitmap. Various techniques exist for interpolating the extra pixels, but they all have a detrimental effect on the image quality—crisp edges can become blurred, and strange artifacts can appear. These problems will be particularly obvious on high resolution output devices such as printers. Even reducing the size of a bitmap causes problems—here there are too many pixels in the bitmap to fit in the space available, so details inevitably become lost. Again there are well-known techniques to mitigate the problems, but none is perfect.

If you use the shape classes in Avalon, your images will not suffer from these problems, because they do not deal with pixels—you are representing your pictures in what is often described as a "vector" format. Vector graphics formats work by specifying the primitive shapes that make up the picture, and describing their positions using coordinates (or "vectors"). A big advantage of vector formats is that when an image comes to be displayed, it can be drawn to look exactly right for the device on which it is being shown. This avoids the resizing artifacts that bitmaps suffer from. It also means that when the image is displayed on a very high resolution device such as a printer (printers typically have at least 10 times the resolution of a screen) a version of the image can be created that takes full advantage of the device's capabilities. Because vector images are represented as primitives and vectors, they can be drawn at any resolution and any scale with minimal loss of precision. (Rendering to any device will involve some loss of precision because no device is perfect. But a vector format itself does not introduce any scaling losses of its own, unlike a bitmap format.) This is why such formats are often described as "scalable."

XAML, with its shape elements, is by no means the first vector format to be invented. In fact Windows has long supported vector graphics in the form of Windows Metafiles and Enhanced Metafiles. (.WMF and .EMF files, respectively. .WMF files date back to 16-bit versions of Windows. .EMF files came along later to support the new drawing primitives that Win32 introduced.) These work by allowing a series of GDI drawing operations to be captured, optionally stored in a file, and later replayed. When used carefully, these offer the scalability benefits common to all vector formats. So you might ask why Longhorn didn't simply carry on using metafiles for vector graphics. The answer is that XAML offers two substantial benefits over metafiles.

First, .WMF and .EMF files are binary formats that are hard to work with. Only a masochist would attempt to create such a file from scratch. In practice it is usually necessary to write some code that will draw the picture you require in order to create a metafile. If this were the only issue it wouldn't be too bad, because some drawing packages are able to export drawings in these formats. (This makes creating simple drawings like the previous example an unnecessarily long-winded process, but it's not as bad as writing code to build a metafile.) However, the problems don't stop with the difficulty of creating a windows metafile.

The second problem is that metafiles are difficult for software to manipulate. If you don't mind treating the drawing as an impenetrable lump of binary to be rendered without modification, much as a bitmap would be drawn, then this is not a big limitation. However, if you want to be able to change individual elements in the drawing, this is extremely hard work. There are APIs that will enumerate the contents of a metafile, and let you modify the way they are rendered, but these are cumbersome to use.

Avalon provides a good solution to both of these problems. Simple drawings are very straightforward to produce, as the previous example shows. (More complex drawings will doubtless be created in full-blown drawing packages, and converted to XAML.) Moreover, because shape elements are just part of the XAML markup tree, they are also extremely easy for the program to access and manipulate. Consider this example:

<DockPanel xmlns="https://schemas.microsoft.com/2003/xaml/"
  xmlns:def="Definition" def:Language="C#">

  <HorizontalSlider DockPanel.Dock="Top" Maximum="200" Width="100"
                    ValueChanged="OnValueChanged"/>

  <Canvas DockPanel.Dock="Fill">
    <Ellipse ID="myEllipse" RadiusX="75" RadiusY="50" CenterX="100" CenterY="80"
             Fill="PaleGreen" Stroke="DarkBlue" StrokeThickness="5"/>
  </Canvas>

  <def:Code><![CDATA[

    void OnValueChanged(object sender,
                        MSAvalon.Windows.Controls.ValueChangedEventArgs args)
    {
      myEllipse.StrokeThickness = new MSAvalon.Windows.Length(args.NewValue);
    }

  ]]>
  </def:Code>

This defines a user interface with a slider at the top, and an ellipse in the main window area, as shown in Figure 5. As the user changes the slider position, the thickness of the ellipse's outline changes. The code accesses the Ellipse element simply by using the name assigned with the ID attribute ("myEllipse").

Aa480164.avalongraphics05(en-us,MSDN.10).gif

Figure 5. Changing UI elements dynamically

This would have been extremely difficult to achieve with metafiles, since there is no easy way to write code to access and change individual elements in a drawing. But with XAML, all drawing elements are part of the user interface tree. By simply giving an element an ID attribute, we can access the corresponding object at runtime (an Ellipse object in this case) and change its properties directly, just as the ValueChanged event handler in this example does.

XAML Drawing Primitives

XAML provides a set of drawing primitives, many of which will seem familiar if you have used GDI+ in the past. These types are all defined in the MSAvalon.Windows.Shapes namespace, and are listed in the table below. Most of these are provided as a convenience—the Path primitive can be used to build most of the other shapes, but it is usually simpler to use the specialized shapes unless the full flexibility of a Path is required.

Table 1. Shape Elements

Element Type Purpose
Ellipse Ellipses and circles
Glyphs A sequence of characters with precisely-specified typographic settings
Line A single straight line
Path Any open or closed shape that can be described as series of lines and curves
Polygon A closed shape with a number of straight edges
Polyline An open shape with a number of straight edges
Rectangle A rectangle or square

All these types derive from a common abstract base class, Shape. Shape provides properties to control aspects common to all drawing primitives. For example, there are properties that control the way in which the shape's outline is drawn, and others that determine the way that interior regions (where present) will be filled.

The Polygon, Polyline, and Path classes all allow complex shapes to be constructed. Polygon and Polyline both define shapes with any number of straight edges, the only difference being that the Polygon defines a closed shape (one with an interior) whereas Polyline defines an open shape. Both of these have a property called Points, which allows a list of vertices to be supplied:

  <Polyline Stroke="Black" Points="0,0 30,0 60,30 60,60 30,60 0,30"/>

or:

  <Polygon Stroke="Black" Points="0,0 30,0 60,30 60,60 30,60 0,30"/>

As this example shows, the list of points is just a series of coordinate pairs in a string. (If you access the Points property from code at runtime, however, it is represented by the PointCollection class, allowing indexed access to a collection of Point structures.) As the output below (Figure 6) shows, the only difference between Polyline and the Polygon is that the Polygon has closed the shape off. (Note that even if you make the first and last vertex of a Polyline coincide, the shape is still considered to be open, so even if you set a Fill color, it will never paint the interior of the shape. Polygons, by contrast, can always be filled.)

Aa480164.avalongraphicsn(en-us,MSDN.10).gif

Figure 6. Polyline vs. Polygon

Path is the most powerful of all of the shapes. It can be used to create any of the other shapes apart from Glyphs. It is used in a similar way to the Polyline and Polygon—just as they have a Points property allowing vertices to be supplied, the Path shape has a Data property that allows outline information to be specified. Because a Path outline can contain both straight and curved elements, the outline data can be a little more complex than the simple list of coordinates used by Polyline and Polygon.

There are two ways of setting a Path element's Data property. You can either use markup, with an element for each item in the path data, or you can use a string representation. Both approaches have the same result. The string format is the more succinct style, but the markup approach is arguably easier to follow. Either representation will cause a GeometryCollection object to be built at runtime, and assigned to the Path's Data property.

The GeometryCollection holds, unsurprisingly, a collection of Geometry objects. Geometry is an abstract class, and most of its concrete derived classes allow the shapes we looked at earlier to be created. For example, the LineGeometry, EllipseGeometry, and RectangleGeometry classes are equivalent to using the Line, Ellipse, and Rectangle shapes. (In fact those shape types are just shorthand for creating a Path shape containing the appropriate kind of Geometry.) For example, this:

  <Ellipse CenterX="80" CenterY="80" RadiusX="50" RadiusY="50"
           Fill="PaleGreen" Stroke="DarkBlue" StrokeThickness="5"/>

is equivalent to this:

  <Path Fill="PaleGreen" Stroke="DarkBlue" StrokeThickness="5">
    <Path.Data>
      <GeometryCollection>
        <EllipseGeometry Center="80, 80" RadiusX="50" RadiusY="50"/>
      </GeometryCollection>
    </Path.Data>
  </Path>

The most powerful Geometry is the PathGeometry class. This is the basis of the Polygon and Polyline elements, but it allows other kinds of shapes to be created too. A PathGeometry may contain multiple shapes. It has a Figures property, which contains a collection of PathFigure objects, each of which represents an individual shape within the Path. Each individual PathFigure shape is determined by a Segments property, which contains a collection of PathSegment objects. Each PathSegment represents a segment of the shape's outline, and there are several classes derived from the abstract PathSegment class, according to whether the segment of the outline should be straight, or one of the various supported curve types. For example, the following XAML shows a Path containing a single figure whose outline has two straight edges and one curved edge:

<Canvas xmlns="https://schemas.microsoft.com/2003/xaml">
  <Path Fill="Blue" Stroke="Black" StrokeThickness="5">
    <Path.Data>
      <GeometryCollection>
        <PathGeometry>
          <PathGeometry.Figures>
            <PathFigureCollection>
              <PathFigure>
                <PathFigure.Segments>
                  <PathSegmentCollection>

                    <StartSegment Point="10,10"/>
                    <LineSegment Point="100,100"/>
                    <BezierSegment Point1="200,200" Point2="10,200" Point3="10,100"/>
                    <CloseSegment />

                  </PathSegmentCollection>
                </PathFigure.Segments>
              </PathFigure>
            </PathFigureCollection>
          </PathGeometry.Figures>
        </PathGeometry>
      </GeometryCollection>
    </Path.Data>
  </Path>
</Canvas>

Aa480164.avalongraphics06(en-us,MSDN.10).gif

Figure 7. A path

The results are shown in Figure 7. In case you are wondering how this path can have two straight edges despite containing only one LineSegment, the second straight edge is added by the CloseSegment—this adds a closing straight line from the end of the BezierSegment back to the start of the figure.

The previous example illustrates how the markup approach to defining a path can rapidly get rather verbose. The equivalent text string version looks like this:

<Canvas xmlns="https://schemas.microsoft.com/2003/xaml">
  <Path Fill="Blue" Stroke="Black" StrokeThickness="5"
        Data="M 10,10 L 100,100 C 200,200 10,200 10,100 Z" />
</Canvas>

This string format for the Data property allows each segment to be represented by a letter and a series of parameters. The letter denotes a command, and the parameters are typically coordinates. Table 2 shows a complete list of commands and their usage. Multiple figures may be introduced by closing the existing figure (with the Z command) and then starting a new one. In this particular example, there is a single figure, where the M command Moves to a starting point, the C command draws a Cubic Bezier segment, and the Z command closes the figure.

Table 2. Path.Data Commands

Command Parameters Usage
M x,y Move to—moves the path to a particular position. The first command must always be a move to.
Z None Close path—ends a figure in the path. (More path data may follow, but it must begin with another move to command.)
L x,y

(may repeat)

Line to—draws a line to the specified point. You may specify any number of coordinate pairs to draw a multi-line segment.
H x Horizontal line to—draws a horizontal line to the specified X position.
V y Vertical line to—draws a vertical line to the specified Y position
C x1,y1 x2,y2 x3,y3

(may repeat)

Curve to—draws a cubic Bezier curve to the specified position. Three sets of coordinates must be provided to define the shape of the curve. You can add multiple curve segments by adding further sets of three coordinates.

When using this text format, coordinates can be specified in one of two forms. You may either use relative coordinates, which are relative to the previous point in the path, or "absolute" coordinates, which are relative to the location of the shape. Relative coordinates are indicated by using a lowercase letter for the command. (Relative coordinates are available only when you use this text format. If you use markup elements to describe the path data, you can only use absolute coordinates.)

Brushes

In the examples shown so far, fill and outline colors have been specified using named colors. This is very convenient when simple colors are all that is required, but Longhorn allows applications to use a wide range of effects in a shape's fill and outline. The Stroke and Fill properties of the Shape class are of type Brush, and Longhorn provides several different types of Brush, providing a variety of visual effects.

Brushes are used in Longhorn to determine how an area of the screen will be filled in. If you are familiar with GDI+, then a Longhorn MSAvalon.Windows.Media.Brush has roughly the same role as a GDI+ System.Drawing.Brush. However, unlike in GDI+, where brushes are typically transient objects used only inside repaint methods, many visual elements in Longhorn provide properties of type Brush that can be set using markup. Most properties for which you might expect to be able to specify a color usually accept any brush. So unlike in Windows Forms, where you are usually limited to plain colors when specifying the foreground and background for a control, in Avalon you can use any brush for the foreground and background.

The use of brushes rather than simple colors allows for a much more interesting user interface design. As well as supporting simple solid color paint, which uses a single uniform color (much like a GDI+ SolidBrush), Avalon offers many more interesting Brush types. LinearGradientPaint offers multi-stage linear fills. RadialGradient offers a gradient fill starting from a point and spreading out to a circular boundary. ImagePaint allows any bitmap to be used as a fill pattern.

The following example shows the use of the LinearGradientBrush to fill the interior of a Polygon. The brush changes color continuously, starting from red, changing to magenta, and finishing on cyan, as specified by the three GradientStop elements.

<Canvas xmlns="https://schemas.microsoft.com/2003/xaml/">
  <Polygon Points="5,5 35,5 65,35 65,65 35,65 5,35" Stroke="Black">
    <Polygon.Fill>
      <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
        <LinearGradientBrush.GradientStops>
          <GradientStopCollection>
            <GradientStop Color="red" Offset="0"/>
            <GradientStop Color="magenta" Offset="0.5"/>
            <GradientStop Color="cyan" Offset="1"/>
          </GradientStopCollection>
        </LinearGradientBrush.GradientStops>
      </LinearGradientBrush>
    </Polygon.Fill>
  </Polygon>
</Canvas>

As you can see in Figure 8, the fill goes from the top left corner of the shape to the bottom right. This is controlled by the StartPoint and EndPoint properties—the StartPoint of 0,0 indicates that the fill should start at the top left corner, and the EndPoint of 1,1 indicates the bottom right.

Aa480164.avalongraphics07(en-us,MSDN.10).gif

Figure 8. A linear gradient brush

Transformations

One of the very useful features of vector graphics is that it is easy to apply transformations to them. Because the position of every part of every visual element is expressed through coordinates, it is fairly straightforward to rotate, scale, translate, or shear those elements.

In Avalon, transformations are applied to shapes using a TransformDecorator. This is an element that can be wrapped around some markup in order to apply a transformation to all of the elements in that markup. For example, consider the following markup:

<Canvas xmlns="https://schemas.microsoft.com/2003/xaml/">
  <TransformDecorator>
    <TransformDecorator.Transform>
      <RotateTransform Angle="45"/>
    </TransformDecorator.Transform>

    <Text>Hello!</Text>

  </TransformDecorator>
</Canvas>

This example contains just one visible element of type Text. But this Text element has been wrapped in a TransformDecorator that applies a 45-degree rotation, as Figure 9 shows. There are a number of different types of transformation that can be applied in this way: RotateTransform, ScaleTransform, TranslateTransform, and SkewTransform allow the most common transformation types to be applied easily. Alternatively you can combine multiple transformations by using a TransformCollection. (This would just contain a series of child transformations in the markup.) Or if you wish to apply a particular transformation matrix, you can use the MatrixTransform.

Aa480164.avalongraphics08(en-us,MSDN.10).gif

Figure 9. A RotateTransform in action

Transformations can be applied to any markup element. For example, rather than rotating a single text element, we could have placed a DockPanel inside the TransformDecorator, and rotated an entire user interface. All of the built-in controls such as text boxes and buttons carry on working perfectly with arbitrary transformations applied. This makes it easy to enlarge a user interface, either to improve readability for accessibility reasons, or because the display device may have a very high resolution. (Displays with 200 dpi resolutions are available. Normal Windows applications are too small to be usable on such displays. But with Avalon, where any user interface can be transformed, it is easy to scale applications up so that they are large enough to be useful, but can still take advantage of the high resolution to improve the detail and clarity of the display.)

Other Goodies

In this article there has been space only to scratch the surface of what Longhorn can offer in the UI. As a quick taster, here are a few of the available features not discussed elsewhere in this article. Visual elements can be animated. Applications can enable interactive editing of the user interface at runtime. Other media types such as audio and video can be integrated into the display. There is support for 3-D visuals. Extensive text handling support is available, making it easy to format the text in the most appropriate way for the medium in which it is being presented. There is also an API for the "visual layer," allowing direct access to the underlying display technology that the markup builds upon.

Conclusion

The new graphics model introduced by Longhorn offers the best aspects of previous approaches to building the UI, such as Win32-style repainting, bitmap backing store, and HTML-based user interfaces. It provides a powerful set of drawing primitives and makes them very straightforward to use through markup, relieving application developers of the more tedious aspects of getting the user interface to appear on screen. At runtime, there is an object model that reflects the structure of the markup, making it very easy to manipulate the display and keep it up-to-date. All of this is built on top of a new display model that removes many of the restrictions of the old Win32 model, enabling much more interesting visual effects with its powerful new composition engine.