Home | All Classes | Main Classes | Annotated | Grouped Classes | Functions |

# Qt 4: The Arthur Paint Subsystem

This document describes Qt 4's approach to painting, and contrasts it with the Qt 3 approach.

- Architecture

- A Look Back at Qt 3
- How Painting is Done in Qt 4
- What's Changed Since Qt 4 Technology Preview 1?
- Enabling Use of GDI+ on Windows

- New Features in the Qt 4 Paint Subsystem

- Linear Gradient Brushes
- Alpha-Blended Drawing
- QPainter and QGLWidget
- Anti-Aliased Edges
- Extensive Use of Native Graphics Operations
- Painter Paths
- Widget Double-Buffering
- Pen and Brush Transformation

- More to Come

## Architecture

The Qt 4 Paint Subsystem is primarly based on the classes QPainter, QPaintDevice and QPaintEngine. QPainter is the class used to perform drawing operations, for example, drawLine() and drawRect(). QPaintDevice is an object that can be painted on using a QPainter. Both QWidget and QPixmap are QPaintDevices. QPaintEngine is the painter's means of drawing to a specific device type.

### A Look Back at Qt 3

In Qt 3, QPainter could be used to draw on widgets and pixmaps. (It could also be used to draw to printers on Windows and Mac OS X). When other paint devices needed to be supported, such as QPrinter on X11, this was done by deriving from QPaintDevice and reimplementing the virtual function QPaintDevice::cmd(). A reimplemented paint device was treated as an external device.

QPainter was capable of recognizing external devices and could serialize each paint operation to the reimplemented cmd() function. This allowed reimplementation of arbitrary devices, but the approach has some disadvantages which we have addressed in Qt 4. One of these is that an external device could not reuse any functionality implemented in QPainter since QPainter is tied to widget/pixmap painting on the current platform. Supporting multiple device backends, such as OpenGL, was therefore inconvenient and not very efficient.

This has led us to devise a more convenient and intuitive API for Qt 4.

### How Painting is Done in Qt 4

In Qt 4 we have introduced the abstract class QPaintEngine. Implementations of this class provide the concrete functionallity needed to draw to a specific device type. The class QPaintEngine is only used internally by QPainter and QPaintDevice and is hidden from application programmers, unless they reimplement their own device type in their own QPaintEngine subclass. Qt currently provides paint engines for the following platforms and API's:

- GDI and GDI+ on Windows
- OpenGL on all platforms
- PostScript on Linux, Unix, and Mac OS X
- QuickDraw and CoreGraphics on Mac OS X
- X11 and the X Render Extension on Linux and Unix systems
- QVFb, VNC and LinuxFb on Qt/Embedded

To implement support for a new backend, you must derive from QPaintEngine and reimplement its virtual functions. You also need to derive from QPaintDevice and reimplement the virtual function QPaintDevice::paintEngine() to tell QPainter which paint engine should be used to draw on this particular device.

The main benefit of this approach is that all painting follows the same painting pipeline which means that adding support for new features and providing default implementations for unsupported ones has become much simpler.

### What's Changed Since Qt 4 Technology Preview 1?

As a result of user feedback and internal discussion, we have made some improvements to the Qt 4 Paint Subsystem:

- The paint engine (`QPaintEngine`) and painter path (`QPainterPath`) now provide floating point accuracy for coordinates. As a result, many painting operations are now more accurate than before, allowing certain types of rendering to be achieved that were not previously possible.
- The integer-based API has been maintained for compatibility with existing classes.
- On X11: Various improvements to rendering quality and performance have been achieved with the XRender extension.

A number of classes have been introduced to provide support for floating point coordinates:

- `QPointF`, `QRectF`, and `QSizeF` are floating point equivalents of `QPoint`, `QRect`, and `QSize`.
- `QPolygon` provides a point array class that is similar to `QPointArray`, but which uses `QPointF` to store the coordinates of each point. Additionally, the contents of a `QPainterPath` object can be flattened to produce a `QPolygon`.
- `QLineF` provides a description of a vector in two-dimensional space.

### Enabling Use of GDI+ on Windows

GDI+ is installed by default on Windows XP, and is available as a downloadable redistributable binary for 98/ME/NT/2000. In order for Qt to take advantage of the features in GDI+ the gdiplus.dll library must be in the PATH.

## New Features in the Qt 4 Paint Subsystem

### Linear Gradient Brushes

With Qt 4 it is possible to fill shapes using a linear gradient brush. A gradient in this case is used to describe the transition from one color at one point to another color at another point. We use the term linear gradient since the transition area follows a line.

Setting a linear gradient brush is done using the QBrush constructor that takes two points and two colors, as sketched below:

        QBrush diagonalGradient(QPoint(0, 0), Qt::red, QPoint(width(), height()), Qt::blue);
        painter.setBrush(diagonalGradient);
        painter.drawRect(0, 0, width(), height());

The code shown above produces a pattern as show in the following pixmap:

### Alpha-Blended Drawing

With Qt 4 we support alpha-blended outlining and filling. The alpha channel of a color is specified through QColor.

        // Specfiy semi-transparent red
        painter.setBrush(QColor(255, 0, 0, 127));
        painter.drawRect(0, 0, width()/2, height());

        // Specify semi-transparend blue
        painter.setBrush(QColor(0, 0, 255, 127));
        painter.drawRect(0, 0, width(), height()/2);

The code shown above produces the following output.

Alpha-blended drawing is supported on Windows, Mac OS X, and on X11 systems that have the X Render extension installed.

### QPainter and QGLWidget

It is now possible to open a QPainter on a QGLWidget as if it were a normal QWidget. One huge benefit from this is that we utilize the high performance of OpenGL for most drawing operations, such as transformations and pixmap drawing.

### Anti-Aliased Edges

On platforms where this is supported by the drawing API's, we provide the option of turning on anti-aliased edges when drawing primitives.

        // One line without anti-aliasing
        painter.drawLine(0, 0, width()/2, height());

        // One line with anti-aliasing
        painter.setRenderHints(QPainter::LineAntialiasing);
        painter.drawLine(width()/2, 0, width()/2, height());

This produces the following output.

Supported backends are currently Mac OS X, Windows if GDI+ is installed, X11 and OpenGL.

### Extensive Use of Native Graphics Operations

In the Qt 4 Paint Subsystem we make more use of native graphics operations. The benefit we gain from this is that these operations can potentially be performed in hardware which gives significant speed improvements.

Among these are native transformations (Windows 2000/XP, Mac OS X, and, OpenGL) making painting with a world matrix much faster. Some pixmap operations have also been moved closer to hardware.

### Painter Paths

A painter path is an object composed of a number of graphical building blocks, such as rectangles, ellipses, lines and curves. A painter path can be used for filling, outlining, and for clipping. The main advantage of painter paths over normal drawing operations is that it is possible to build up non-linear shapes which can be drawn later one go.

Building blocks can be joined in closed subpaths, such as a rectangle or an ellipse, or they can exist independently as unclosed subpaths, although an unclosed path will not be filled.

Below is a code example on how a path can be used. The painter in this case has a pen width of 3 and a light blue brush. We first add a rectangle, which becomes a closed subpath. We then add two bezier curves, and finally draw the entire path.

        QPainterPath path;
        path.addRect(20, 20, 60, 60);
        path.addBezier(0, 0,  99, 0,  50, 50,  99, 99);
        path.addBezier(99, 99,  0, 99,  50, 50,  0, 0);
        painter.drawPath(path);

The code above produces the following output:

### Widget Double-Buffering

In Qt 4, all widgets are double-buffered by default.

In previous versions of Qt double-buffereing was acheived by painting to an off-screen pixmap then copying the pixmap to the screen. e.g.

        QPixmap buffer(size());
        QPainter painter(&buffer);

        // Paint code here

        painter.end();
        bitBlt(this, 0, 0, &buffer);

Since the double-buffering is handled by QWidget internally this now becomes:

        QPainter painter(this);

        // Paint code here

        painter.end();

Double-buffering is turned on by default, but can be turned off by setting the widget attribute Qt::WA_PaintOnScreen.

        unbufferedWidget->setAttribute(Qt::WA_PaintOnScreen);

### Pen and Brush Transformation

In Qt 3, pens and brushes weren't affected by the painter's transformation matrix. For example, if you drew a rectangle with a pen width of 1 using a scaled painter, the resulting line width would still be 1. This made it difficult to implement features such as zooming and high-resolution printing.

In Qt 4, pens and brushes honor the painter's transformation matrix.

Note that this feature is still in development and not yet supported on all platforms.

## More to Come

The Qt 4 Paint Subsystem is mostly completed, but there is still work being done.

Part of the work in progress is the finalization of the rendering pipeline for all platforms, so that all features are supported and look the same on all platforms. Among the currently unfinished features are these:

- Gradients cannot have alpha color on X11.
- Support for querying for supported features (such as if antialiasing is available).
- Support for a floating point API in `QPainter` while maintaining compatibility with the existing integer-based functions.

[Back to the Technology Preview page]

See also QBrush.