282 q->d_ptr->state->ww = q->d_ptr->state->vw = widget->width(); |
282 q->d_ptr->state->ww = q->d_ptr->state->vw = widget->width(); |
283 q->d_ptr->state->wh = q->d_ptr->state->vh = widget->height(); |
283 q->d_ptr->state->wh = q->d_ptr->state->vh = widget->height(); |
284 |
284 |
285 // Update matrix. |
285 // Update matrix. |
286 if (q->d_ptr->state->WxF) { |
286 if (q->d_ptr->state->WxF) { |
287 q->d_ptr->state->redirectionMatrix *= q->d_ptr->state->worldMatrix; |
287 q->d_ptr->state->redirectionMatrix = q->d_ptr->state->matrix; |
288 q->d_ptr->state->redirectionMatrix.translate(-offset.x(), -offset.y()); |
288 q->d_ptr->state->redirectionMatrix.translate(-offset.x(), -offset.y()); |
289 q->d_ptr->state->worldMatrix = QTransform(); |
289 q->d_ptr->state->worldMatrix = QTransform(); |
290 q->d_ptr->state->WxF = false; |
290 q->d_ptr->state->WxF = false; |
291 } else { |
291 } else { |
292 q->d_ptr->state->redirectionMatrix = QTransform::fromTranslate(-offset.x(), -offset.y()); |
292 q->d_ptr->state->redirectionMatrix = QTransform::fromTranslate(-offset.x(), -offset.y()); |
1314 large pen widths or scales the outline error increases. To generate outlines with |
1314 large pen widths or scales the outline error increases. To generate outlines with |
1315 smaller errors it is possible to use the QPainterPathStroker class, which has the |
1315 smaller errors it is possible to use the QPainterPathStroker class, which has the |
1316 setCurveThreshold member function which let's the user specify the error tolerance. |
1316 setCurveThreshold member function which let's the user specify the error tolerance. |
1317 Another workaround is to convert the paths to polygons first and then draw the |
1317 Another workaround is to convert the paths to polygons first and then draw the |
1318 polygons instead. |
1318 polygons instead. |
|
1319 |
|
1320 \section1 Performance |
|
1321 |
|
1322 QPainter is a rich framework that allows developers to do a great |
|
1323 variety of graphical operations, such as gradients, composition |
|
1324 modes and vector graphics. And QPainter can do this across a |
|
1325 variety of different hardware and software stacks. Naturally the |
|
1326 underlying combination of hardware and software has some |
|
1327 implications for performance, and ensuring that every single |
|
1328 operation is fast in combination with all the various combinations |
|
1329 of composition modes, brushes, clipping, transformation, etc, is |
|
1330 close to an impossible task because of the number of |
|
1331 permutations. As a compromise we have selected a subset of the |
|
1332 QPainter API and backends, were performance is guaranteed to be as |
|
1333 good as we can sensibly get it for the given combination of |
|
1334 hardware and software. |
|
1335 |
|
1336 The backends we focus on as high-performance engines are: |
|
1337 |
|
1338 \list |
|
1339 |
|
1340 \o Raster - This backend implements all rendering in pure software |
|
1341 and is always used to render into QImages. For optimal performance |
|
1342 only use the format types QImage::Format_ARGB32_Premultiplied, |
|
1343 QImage::Format_RGB32 or QImage::Format_RGB16. Any other format, |
|
1344 including QImage::Format_ARGB32, has significantly worse |
|
1345 performance. This engine is also used by default on Windows and on |
|
1346 QWS. It can be used as default graphics system on any |
|
1347 OS/hardware/software combination by passing \c {-graphicssystem |
|
1348 raster} on the command line |
|
1349 |
|
1350 \o OpenGL 2.0 (ES) - This backend is the primary backend for |
|
1351 hardware accelerated graphics. It can be run on desktop machines |
|
1352 and embedded devices supporting the OpenGL 2.0 or OpenGL/ES 2.0 |
|
1353 specification. This includes most graphics chips produced in the |
|
1354 last couple of years. The engine can be enabled by using QPainter |
|
1355 onto a QGLWidget or by passing \c {-graphicssystem opengl} on the |
|
1356 command line when the underlying system supports it. |
|
1357 |
|
1358 \o OpenVG - This backend implements the Khronos standard for 2D |
|
1359 and Vector Graphics. It is primarily for embedded devices with |
|
1360 hardware support for OpenVG. The engine can be enabled by |
|
1361 passing \c {-graphicssystem openvg} on the command line when |
|
1362 the underlying system supports it. |
|
1363 |
|
1364 \endlist |
|
1365 |
|
1366 These operations are: |
|
1367 |
|
1368 \list |
|
1369 |
|
1370 \o Simple transformations, meaning translation and scaling, pluss |
|
1371 0, 90, 180, 270 degree rotations. |
|
1372 |
|
1373 \o \c drawPixmap() in combination with simple transformations and |
|
1374 opacity with non-smooth transformation mode |
|
1375 (\c QPainter::SmoothPixmapTransform not enabled as a render hint). |
|
1376 |
|
1377 \o Text drawing with regular font sizes with simple |
|
1378 transformations with solid colors using no or 8-bit antialiasing. |
|
1379 |
|
1380 \o Rectangle fills with solid color, two-color linear gradients |
|
1381 and simple transforms. |
|
1382 |
|
1383 \o Rectangular clipping with simple transformations and intersect |
|
1384 clip. |
|
1385 |
|
1386 \o Composition Modes \c QPainter::CompositionMode_Source and |
|
1387 QPainter::CompositionMode_SourceOver |
|
1388 |
|
1389 \o Rounded rectangle filling using solid color and two-color |
|
1390 linear gradients fills. |
|
1391 |
|
1392 \o 3x3 patched pixmaps, via qDrawBorderPixmap. |
|
1393 |
|
1394 \endlist |
|
1395 |
|
1396 This list gives an indication of which features to safely use in |
|
1397 an application where performance is critical. For certain setups, |
|
1398 other operations may be fast too, but before making extensive use |
|
1399 of them, it is recommended to benchmark and verify them on the |
|
1400 system where the software will run in the end. There are also |
|
1401 cases where expensive operations are ok to use, for instance when |
|
1402 the result is cached in a QPixmap. |
1319 |
1403 |
1320 \sa QPaintDevice, QPaintEngine, {QtSvg Module}, {Basic Drawing Example}, |
1404 \sa QPaintDevice, QPaintEngine, {QtSvg Module}, {Basic Drawing Example}, |
1321 {Drawing Utility Functions} |
1405 {Drawing Utility Functions} |
1322 */ |
1406 */ |
1323 |
1407 |
3784 } |
3869 } |
3785 |
3870 |
3786 if (d->state->pen == pen) |
3871 if (d->state->pen == pen) |
3787 return; |
3872 return; |
3788 |
3873 |
|
3874 d->state->pen = pen; |
|
3875 |
3789 if (d->extended) { |
3876 if (d->extended) { |
3790 d->state->pen = pen; |
|
3791 d->checkEmulation(); |
3877 d->checkEmulation(); |
3792 d->extended->penChanged(); |
3878 d->extended->penChanged(); |
3793 return; |
3879 return; |
3794 } |
3880 } |
3795 |
3881 |
3796 // Do some checks to see if we are the same pen. |
|
3797 Qt::PenStyle currentStyle = d->state->pen.style(); |
|
3798 if (currentStyle == pen.style() && currentStyle != Qt::CustomDashLine) { |
|
3799 if (currentStyle == Qt::NoPen || |
|
3800 (d->state->pen.isSolid() && pen.isSolid() |
|
3801 && d->state->pen.color() == pen.color() |
|
3802 && d->state->pen.widthF() == pen.widthF() |
|
3803 && d->state->pen.capStyle() == pen.capStyle() |
|
3804 && d->state->pen.joinStyle() == pen.joinStyle() |
|
3805 && d->state->pen.isCosmetic() == pen.isCosmetic())) |
|
3806 return; |
|
3807 } |
|
3808 |
|
3809 d->state->pen = pen; |
|
3810 d->state->dirtyFlags |= QPaintEngine::DirtyPen; |
3882 d->state->dirtyFlags |= QPaintEngine::DirtyPen; |
3811 } |
3883 } |
3812 |
3884 |
3813 /*! |
3885 /*! |
3814 \overload |
3886 \overload |
5917 |
5986 |
5918 The underline and strikeout parameters of the text items font are |
5987 The underline and strikeout parameters of the text items font are |
5919 ignored aswell. You'll need to pass in the correct flags to get |
5988 ignored aswell. You'll need to pass in the correct flags to get |
5920 underlining and strikeout. |
5989 underlining and strikeout. |
5921 */ |
5990 */ |
5922 static QPainterPath generateWavyPath(qreal minWidth, qreal maxRadius, QPaintDevice *device) |
5991 |
5923 { |
5992 static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen) |
5924 extern int qt_defaultDpi(); |
5993 { |
|
5994 const qreal radiusBase = qMax(qreal(1), maxRadius); |
|
5995 |
|
5996 QString key = QLatin1String("WaveUnderline-"); |
|
5997 key += pen.color().name(); |
|
5998 key += QLatin1Char('-'); |
|
5999 key += QString::number(radiusBase); |
|
6000 |
|
6001 QPixmap pixmap; |
|
6002 if (QPixmapCache::find(key, pixmap)) |
|
6003 return pixmap; |
|
6004 |
|
6005 const qreal halfPeriod = qMax(qreal(2), qreal(radiusBase * 1.61803399)); // the golden ratio |
|
6006 const int width = qCeil(100 / (2 * halfPeriod)) * (2 * halfPeriod); |
|
6007 const int radius = qFloor(radiusBase); |
|
6008 |
5925 QPainterPath path; |
6009 QPainterPath path; |
5926 |
6010 |
5927 bool up = true; |
6011 qreal xs = 0; |
5928 const qreal radius = qMax(qreal(.5), qMin(qreal(1.25 * device->logicalDpiY() / qt_defaultDpi()), maxRadius)); |
6012 qreal ys = radius; |
5929 qreal xs, ys; |
6013 |
5930 int i = 0; |
6014 while (xs < width) { |
5931 path.moveTo(0, radius); |
6015 xs += halfPeriod; |
5932 do { |
6016 ys = -ys; |
5933 xs = i*(2*radius); |
6017 path.quadTo(xs - halfPeriod / 2, ys, xs, 0); |
5934 ys = 0; |
6018 } |
5935 |
6019 |
5936 qreal remaining = minWidth - xs; |
6020 pixmap = QPixmap(width, radius * 2); |
5937 qreal angle = 180; |
6021 pixmap.fill(Qt::transparent); |
5938 |
6022 { |
5939 // cut-off at the last arc segment |
6023 QPen wavePen = pen; |
5940 if (remaining < 2 * radius) |
6024 wavePen.setCapStyle(Qt::SquareCap); |
5941 angle = 180 * remaining / (2 * radius); |
6025 |
5942 |
6026 // This is to protect against making the line too fat, as happens on Mac OS X |
5943 path.arcTo(xs, ys, 2*radius, 2*radius, 180, up ? angle : -angle); |
6027 // due to it having a rather thick width for the regular underline. |
5944 |
6028 const qreal maxPenWidth = .8 * radius; |
5945 up = !up; |
6029 if (wavePen.widthF() > maxPenWidth) |
5946 ++i; |
6030 wavePen.setWidth(maxPenWidth); |
5947 } while (xs + 2*radius < minWidth); |
6031 |
5948 |
6032 QPainter imgPainter(&pixmap); |
5949 return path; |
6033 imgPainter.setPen(wavePen); |
|
6034 imgPainter.setRenderHint(QPainter::Antialiasing); |
|
6035 imgPainter.translate(0, radius); |
|
6036 imgPainter.drawPath(path); |
|
6037 } |
|
6038 |
|
6039 QPixmapCache::insert(key, pixmap); |
|
6040 |
|
6041 return pixmap; |
5950 } |
6042 } |
5951 |
6043 |
5952 static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QTextItemInt &ti) |
6044 static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QTextItemInt &ti) |
5953 { |
6045 { |
5954 QTextCharFormat::UnderlineStyle underlineStyle = ti.underlineStyle; |
6046 QTextCharFormat::UnderlineStyle underlineStyle = ti.underlineStyle; |
5965 pen.setStyle(Qt::SolidLine); |
6057 pen.setStyle(Qt::SolidLine); |
5966 pen.setWidthF(fe->lineThickness().toReal()); |
6058 pen.setWidthF(fe->lineThickness().toReal()); |
5967 pen.setCapStyle(Qt::FlatCap); |
6059 pen.setCapStyle(Qt::FlatCap); |
5968 |
6060 |
5969 QLineF line(pos.x(), pos.y(), pos.x() + ti.width.toReal(), pos.y()); |
6061 QLineF line(pos.x(), pos.y(), pos.x() + ti.width.toReal(), pos.y()); |
|
6062 |
|
6063 const qreal underlineOffset = fe->underlinePosition().toReal(); |
5970 // deliberately ceil the offset to avoid the underline coming too close to |
6064 // deliberately ceil the offset to avoid the underline coming too close to |
5971 // the text above it. |
6065 // the text above it. |
5972 const qreal underlinePos = pos.y() + qCeil(fe->underlinePosition().toReal()); |
6066 const qreal underlinePos = pos.y() + qCeil(underlineOffset); |
5973 |
6067 |
5974 if (underlineStyle == QTextCharFormat::SpellCheckUnderline) { |
6068 if (underlineStyle == QTextCharFormat::SpellCheckUnderline) { |
5975 underlineStyle = QTextCharFormat::UnderlineStyle(QApplication::style()->styleHint(QStyle::SH_SpellCheckUnderlineStyle)); |
6069 underlineStyle = QTextCharFormat::UnderlineStyle(QApplication::style()->styleHint(QStyle::SH_SpellCheckUnderlineStyle)); |
5976 } |
6070 } |
5977 |
6071 |
5978 if (underlineStyle == QTextCharFormat::WaveUnderline) { |
6072 if (underlineStyle == QTextCharFormat::WaveUnderline) { |
5979 painter->save(); |
6073 painter->save(); |
5980 painter->setRenderHint(QPainter::Antialiasing); |
6074 painter->translate(0, pos.y() + 1); |
5981 painter->translate(pos.x(), underlinePos); |
|
5982 |
6075 |
5983 QColor uc = ti.charFormat.underlineColor(); |
6076 QColor uc = ti.charFormat.underlineColor(); |
5984 if (uc.isValid()) |
6077 if (uc.isValid()) |
5985 painter->setPen(uc); |
6078 pen.setColor(uc); |
5986 |
6079 |
5987 painter->drawPath(generateWavyPath(ti.width.toReal(), |
6080 // Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms |
5988 fe->underlinePosition().toReal(), |
6081 const QPixmap wave = generateWavyPixmap(qMax(underlineOffset, pen.widthF()), pen); |
5989 painter->device())); |
6082 const int descent = (int) ti.descent.toReal(); |
|
6083 |
|
6084 painter->setBrushOrigin(painter->brushOrigin().x(), 0); |
|
6085 painter->fillRect(pos.x(), 0, qCeil(ti.width.toReal()), qMin(wave.height(), descent), wave); |
5990 painter->restore(); |
6086 painter->restore(); |
5991 } else if (underlineStyle != QTextCharFormat::NoUnderline) { |
6087 } else if (underlineStyle != QTextCharFormat::NoUnderline) { |
5992 QLineF underLine(line.x1(), underlinePos, line.x2(), underlinePos); |
6088 QLineF underLine(line.x1(), underlinePos, line.x2(), underlinePos); |
5993 |
6089 |
5994 QColor uc = ti.charFormat.underlineColor(); |
6090 QColor uc = ti.charFormat.underlineColor(); |