XOR Considered Harmful

This article was first published in 2002 by Reach magazine under the title "XOR No More". It is republished here by their permission.

.NET provides a new API for drawing operations called GDI+. It provides significantly more advanced drawing facilities than its predecessor, GDI, but it does not support an XOR drawing mode. This upsets some developers, but GDI+ provides better alternatives for the scenarios in which XOR was usually used with classic GDI programming.

Why XOR?

Classic GDI allowed the screen to be painted using an XOR raster operation (R2_XORPEN). The resulting colours would be determined by performing a bitwise exclusive or operation with the colour already on screen and the colour being drawn with. The upshot is that if you draw the same thing in the same place twice, you end up back where you started: the second drawing operation undoes the first. This makes it very easy to draw a shape, and then to erase it shortly afterwards by drawing it a second time.

The traditional applications for XOR have been temporary overlays, such as selection outlines. It tends to be used when the mouse is performing a drag operation, to indicate what items will be moved to where, or which items will be selected. The advantage of using XOR drawing is that it is fast: it does not require the window’s entire contents to be redrawn in order to move or remove the overlay. High performance is important when tracking mouse movements, since the application will feel sluggish if it fails to keep up with the mouse.

Windows Forms provides very limited support for this kind of drawing. GDI+ does not support it at all, but the System.Windows.Forms.ControlPaint class supplies three methods which bypass GDI+ in order to perform XOR drawing. The methods are DrawReversibleLine, DrawReversibleFrame, and FillReversibleRectangle, which draw lines, and empty and filled rectangles respectively. You cannot specify the colour—the nature of XOR drawing makes it impossible to guarantee which colour will be shown in all cases. Instead you must specify the colour of the background over which the item will be drawn, and the methods will select a colour which will be visible when drawn with XOR over that particular background.

These methods draw onto the screen, not into a control. This means that you will normally need to call your control’s PointToScreen or RectangleToScreen method to get the right coordinates. You will also need to perform your own clipping if you only want to draw into your own window.

Why not XOR?

The main problem with XOR drawing is alluded to in the previous section—it is not possible to guarantee which colour will appear on screen. The resultant colour is influenced both by the colour you choose to draw with and also the colour that is already present on screen, meaning that any area that is painted with an XOR operation will have different colours according to what was there before. The exact results are determined by the colour depth of the display, and with 8-bit displays, the current palette settings will also have an impact.

Another problem with XOR drawing is that it tends to flicker. Because all drawing is being done directly onto the screen, we cannot take advantage of Windows Forms’ automatic double buffering. Whilst certain tricks can be employed to reduce the flicker, it can never be entirely eliminated when drawing with XOR.

It is also extremely easy to get redraw bugs with XOR-based overlays. These most often manifest themselves when a redraw operation occurs whilst an overlay is visible. For example, an email notification might pop-up and disappear whilst an overlay is shown, causing the area under the pop-up to be redrawn. If overlay was larger than the area that gets redrawn, it becomes difficult to put the overlay back reliably, because the relevant ControlPaint methods provide no way to set a clip rectangle. Many applications that use XOR drawing fail to deal with this, and simply leave a mess in the window when this happens.

Avoiding XOR

Given the restrictions and pitfalls, XOR-based drawing would have to look pretty compelling compared to the alternative for it to be worth using. So let us consider the alternative. Rather than using XOR, we can just draw the overlay in our application’s normal redraw code (the OnPaint method, or Paint event handler). We simply repaint the control (i.e. call Invalidate()) whenever the overlay needs to be moved or removed.

This technique has several advantages. First, it means we can draw in whatever colour we like. If we will be drawing over a background with a wide variety of colours, we should draw the overlay in multiple colours; an alternating pale and dark outline is a good choice. (This is most easily achieved by using a HatchBrush, which lets us draw patterns with two colours. The DarkDownwardDiagonal hatch style works well for selection outlines.) This ensures that the outline will be visible over both dark and light backgrounds; selection outlines drawn using XOR have an unhelpful tendency to be invisible over certain background colours.

We can also draw using any style supported by GDI+, and with shapes more exotic than simple lines or rectangles. For example, we could draw a transparent version of the items being dragged, like current version of Windows Explorer does when dragging files.

Also, because we are drawing using a normal Graphics object for our control, our drawing will automatically be clipped, and we will not need to perform any coordinate translation. Furthermore, because we are doing the drawing in the normal OnPaint method or Paint event handler, our overlay will continue to be drawn correctly even if any unexpected redraws occur (e.g. due to email notification pop-ups). So this approach is intrinsically less likely to suffer from redraw bugs.

So given all of these advantages, why would we ever use the much more troublesome and restrictive XOR approach? The only reason is that this approach is more CPU intensive. Instead of just drawing the overlay, everything underneath the overlay must be redrawn every time the overlay needs to be moved. (This doesn’t necessarily mean redrawing the entire control—the Control class’s Invalidate method allows us to be selective about which areas are redrawn.) This could potentially lead to even more flicker than XOR-based overlay drawing, but that is easily addressed by enabling double buffering on the relevant control. In fact double buffering is the superior solution for avoiding flicker because it will eliminate it entirely, which cannot be achieved with an XOR solution.

So in practice XOR has just one advantage: speed. But experience shows that for the vast majority of applications this is not an issue. Redrawing used to be a slow operation, and back in the days when Windows 3.1 shipped, XOR-based drawing was the only realistic way of drawing an overlay fast enough to keep up with the mouse. But machines are now substantially faster, and it is entirely practical to repaint everything under the overlay and still track the mouse with no perceptible lag. In fact a modern PC is fast enough to use advanced effects such as transparency on the overlay and still keep up. Given how much better eschewing XOR can make an application look and feel, there really is no excuse for using XOR any more.


Copyright © 2002-2024, Interact Software Ltd. Please direct all Web site inquiries to webmaster@interact-sw.co.uk