Development/Tutorials/Graphics/Performance: Difference between revisions

From KDE TechBase
(New page: == QPainter::setOpacity() == Never call QPainter::setOpacity() when painting on a QPixmap or a QWidget. QPainter::setOpacity() effectively disables all hardware acceleration, causing all ...)
 
(Expand on setOpacity() a bit, and fix the headings.)
Line 7: Line 7:
path is the kiss of death.
path is the kiss of death.


'''An alternative approach to the problem'''
=== An alternative approach to the problem ===
 
If you're drawing something with a solid color, such as a QPainterPath,
a line, a rectangle etc., the easiest and also the fastest solution is to just change the alpha value of the color you're using, like this:
 
<code cppqt>
QColor c = color;
c.setAlphaF(c.alphaF() * opacity);
</code>
 
If you're using pixmaps, gradients or brushes that you can't easily change the opacity of, you should use the following method instead.


Create a temporary pixmap, initialize it to fully transparent, and then draw
Create a temporary pixmap, initialize it to fully transparent, and then draw
Line 46: Line 56:
a very expensive operation).
a very expensive operation).


'''The right way to do it'''
=== The right way to do it ===


Check if the pixmap has an alpha channel, using QPixmap::hasAlphaChannel().
Check if the pixmap has an alpha channel, using QPixmap::hasAlphaChannel().

Revision as of 20:08, 31 March 2008

QPainter::setOpacity()

Never call QPainter::setOpacity() when painting on a QPixmap or a QWidget. QPainter::setOpacity() effectively disables all hardware acceleration, causing all subsequent rendering operations to be done in software. This means that a call to QPainter::setOpacity() in a performance critical path is the kiss of death.

An alternative approach to the problem

If you're drawing something with a solid color, such as a QPainterPath, a line, a rectangle etc., the easiest and also the fastest solution is to just change the alpha value of the color you're using, like this:

QColor c = color; c.setAlphaF(c.alphaF() * opacity);

If you're using pixmaps, gradients or brushes that you can't easily change the opacity of, you should use the following method instead.

Create a temporary pixmap, initialize it to fully transparent, and then draw on it with normal opacity. When you're done drawing on it, use the DestinationIn composition mode to reduce the alpha of the pixmap, and then draw the pixmap itself where you want the semi-transparent contents. If you need the same content again (for example when you're doing a fade out or fade in animation), it might pay off to cache the fully opaque version.

QPixmap pixmap(size); pixmap.fill(Qt::transparent);

QPainter p; p.begin(&pixmap);

// Do the rendering with full opacity

p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.fillRect(pixmap.rect(), QColor(0, 0, 0, opacity)); p.end();

painter->drawPixmap(0, 0, pixmap);

Note that you can check if setOpacity() is fast by calling painter->paintEngine()->hasFeature(QPaintEngine::ConstantOpacity), but my recommendation is to avoid it completely since it won't be fast on our main platform with the current Qt version (4.4 beta).


QPixmap::setAlphaChannel()

Never use QPixmap::setAlphaChannel() to make a pixmap semi-transparent. In fact you should never ever call QPixmap::setAlphaChannel() for any reason if you can avoid it. This function will convert both pixmaps to images (this involves two blocking synchronous calls to the X server, plus expensive image transport over the socket for each call), expensive format conversion of both images, copying the alpha channel one pixel at a time from the alpha image to the other image, followed by a final conversion of the resulting image back to a pixmap (which is also a very expensive operation).

The right way to do it

Check if the pixmap has an alpha channel, using QPixmap::hasAlphaChannel(). If it has one, start a QPainter on the pixmap, and reduce the alpha using the DestinationIn composition mode. If the pixmap doesn't have an alpha channel, you're going to have to create a new pixmap that has one and copy the content of the original pixmap into it, like this:

QPixmap temp(pixmap.size()); temp.fill(Qt::transparent);

QPainter p(&temp); p.setCompositionMode(QPainter::CompositionMode_Source); p.drawPixmap(0, 0, pixmap); p.setCompositionMode(QPainter::CompositionMode_DestinationIn); p.fillRect(temp.rect(), QColor(0, 0, 0, opacity)); p.end();

pixmap = temp;