Entries from July 2011 ↓

Drawing scalable pixels in the Qt Graphics View framework

Say you have a custom QGraphicsItem that you would like to draw pixel by pixel, and want it to appear pixelated when that item is scaled (or the QGraphicsView it is being displayed on, or whatever). At first, you might try drawing your pixels with QPainter::drawPoint():

void MyItem::paint(
    QPainter* painter,
    const QStyleOptionGraphicsItem* option,
    QWidget* widget)
    painter->drawPoint(x, y);

If you don’t mess with the width of the QPainter’s QPen, that won’t work. Your pixels will always be the size of 1 display pixel, no matter the item’s or the view’s scaling factor. That happens because the default pen width is 0, which guarantees points, outlines, etc. are drawn 1 pixel thick, no matter the scale. Notice the single pixel drawn on the following image, which shows a scaled 16×16 QGraphicsItem:

QPainter::drawPoint will always draw 1-pixel points no matter the scale, if the pen width is set to 0.

But guess what? Setting the pen width to 1 won’t do it either, at least not without a little extra effort. Things will start to look weird. A look into QGraphicsItem’s documentation reveals that QPainter will draw things half outside the pen’s outline, and half inside.

There are two options here:

  1. Set the pen width to 1 and compensate for the way QPainter draws things with its QPen; or
  2. Find another way to draw single pixel-sized… pixels.

The first option can be achieved like this:

QPen pen;
painter->drawPoint(QPointF(x + 0.5,y + 0.5));

But the results aren’t the best. Take a look at the following screenshot. It shows a QGraphicsView with its background QBrush set to black. There is a single 16×16 QGraphicsItem that draws a 16×16 white rectangle and a single red point at it’s bottom right corner. Notice that the red point is off the rectangle’s right border by one pixel. I’m not sure about this, but I think it might be due to floating-point error.

Drawing single pixels in QGraphicsItem::paint with a pen width of 1 does not yield the best possible results.

It seems that the most appropriate way to achieve the desired result is to draw a filled 1×1 rectangle, as in:

painter->fillRect(x, y, 1, 1, Qt::red);

Notice that you should leave/set the pen width to 0. As you can see below, there is no error like in the previous approach:

A better way to draw single pixels in QGraphicsItem::paint is by drawing 1x1 filled rectangles width the pen width set to 0.

In conclusion, you should draw single pixels by drawing filled 1×1 rectangles with a pen width of 0, instead of drawings points with a pen width of 1.

Fond Memories

When I was 9 years old, I used to go to my mom’s workplace to talk to some IT guys who were working there. Eventually, one of them started teaching me BASIC, getting me started into programming and fulfilling my desire to understand how people made “stuff appear on the screen”.

Nowadays I know how bad a language BASIC is, but I eventually get nostalgic. I still sometimes feel like opening an MS-DOS prompt on Microsoft Windows 95 and firing up QBasic 1.0, just to have hours of fun printing things on the screen and making the computer produce all sorts of beeps. Those were really nice days.