src/gui/painting/qpainter.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
2:56cd8111b7f7 3:41300fa6a67c
   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 
  2273 */
  2357 */
  2274 
  2358 
  2275 /*!
  2359 /*!
  2276     Sets the composition mode to the given \a mode.
  2360     Sets the composition mode to the given \a mode.
  2277 
  2361 
  2278     \warning You can only set the composition mode for QPainter
  2362     \warning Only a QPainter operating on a QImage fully supports all
  2279     objects that operates on a QImage.
  2363     composition modes. The RasterOp modes are supported for X11 as
       
  2364     described in compositionMode().
  2280 
  2365 
  2281     \sa compositionMode()
  2366     \sa compositionMode()
  2282 */
  2367 */
  2283 void QPainter::setCompositionMode(CompositionMode mode)
  2368 void QPainter::setCompositionMode(CompositionMode mode)
  2284 {
  2369 {
  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
  3885     if (d->extended) {
  3957     if (d->extended) {
  3886         d->state->brush = brush;
  3958         d->state->brush = brush;
  3887         d->checkEmulation();
  3959         d->checkEmulation();
  3888         d->extended->brushChanged();
  3960         d->extended->brushChanged();
  3889         return;
  3961         return;
  3890     }
       
  3891 
       
  3892     Qt::BrushStyle currentStyle = d->state->brush.style();
       
  3893     if (currentStyle == brush.style()) {
       
  3894         if (currentStyle == Qt::NoBrush
       
  3895             || (currentStyle == Qt::SolidPattern
       
  3896                 && d->state->brush.color() == brush.color()))
       
  3897             return;
       
  3898     }
  3962     }
  3899 
  3963 
  3900     d->state->brush = brush;
  3964     d->state->brush = brush;
  3901     d->state->dirtyFlags |= QPaintEngine::DirtyBrush;
  3965     d->state->dirtyFlags |= QPaintEngine::DirtyBrush;
  3902 }
  3966 }
  5161                pm.width(), pm.height());
  5225                pm.width(), pm.height());
  5162 #endif
  5226 #endif
  5163 
  5227 
  5164     Q_D(QPainter);
  5228     Q_D(QPainter);
  5165 
  5229 
  5166     if (!d->engine)
  5230     if (!d->engine || pm.isNull())
  5167         return;
  5231         return;
  5168 
  5232 
  5169 #ifndef QT_NO_DEBUG
  5233 #ifndef QT_NO_DEBUG
  5170     qt_painter_thread_test(d->device->devType(), "drawPixmap()");
  5234     qt_painter_thread_test(d->device->devType(), "drawPixmap()");
  5171 #endif
  5235 #endif
  5904     \overload
  5968     \overload
  5905 
  5969 
  5906     Draws the text item \a ti at position \a p.
  5970     Draws the text item \a ti at position \a p.
  5907 */
  5971 */
  5908 
  5972 
  5909 /*! \internal
  5973 /*!
       
  5974     \fn void QPainter::drawTextItem(const QPointF &p, const QTextItem &ti)
       
  5975 
       
  5976     \internal
       
  5977     \since 4.1
       
  5978 
  5910     Draws the text item \a ti at position \a p.
  5979     Draws the text item \a ti at position \a p.
  5911 
  5980 
  5912     This method ignores the painters background mode and
  5981     This method ignores the painters background mode and
  5913     color. drawText and qt_format_text have to do it themselves, as
  5982     color. drawText and qt_format_text have to do it themselves, as
  5914     only they know the extents of the complete string.
  5983     only they know the extents of the complete string.
  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();
  6019 
  6115 
  6020     painter->setPen(oldPen);
  6116     painter->setPen(oldPen);
  6021     painter->setBrush(oldBrush);
  6117     painter->setBrush(oldBrush);
  6022 }
  6118 }
  6023 
  6119 
  6024 /*!
       
  6025     \internal
       
  6026     \since 4.1
       
  6027 */
       
  6028 void QPainter::drawTextItem(const QPointF &p, const QTextItem &_ti)
  6120 void QPainter::drawTextItem(const QPointF &p, const QTextItem &_ti)
  6029 {
  6121 {
  6030 #ifdef QT_DEBUG_DRAW
  6122 #ifdef QT_DEBUG_DRAW
  6031     if (qt_show_painter_debug_output)
  6123     if (qt_show_painter_debug_output)
  6032         printf("QPainter::drawTextItem(), pos=[%.f,%.f], str='%s'\n",
  6124         printf("QPainter::drawTextItem(), pos=[%.f,%.f], str='%s'\n",
  7601             l.setLineWidth(lineWidth);
  7693             l.setLineWidth(lineWidth);
  7602             height += leading;
  7694             height += leading;
  7603             l.setPosition(QPointF(0., height));
  7695             l.setPosition(QPointF(0., height));
  7604             height += l.height();
  7696             height += l.height();
  7605             width = qMax(width, l.naturalTextWidth());
  7697             width = qMax(width, l.naturalTextWidth());
  7606             if (!brect && height >= r.height())
  7698             if (!dontclip && !brect && height >= r.height())
  7607                 break;
  7699                 break;
  7608         }
  7700         }
  7609         textLayout.endLayout();
  7701         textLayout.endLayout();
  7610     }
  7702     }
  7611 
  7703 
  7620             if (type <= QTransform::TxScale) {
  7712             if (type <= QTransform::TxScale) {
  7621                 // do the rounding manually to work around inconsistencies
  7713                 // do the rounding manually to work around inconsistencies
  7622                 // in the paint engines when drawing on floating point offsets
  7714                 // in the paint engines when drawing on floating point offsets
  7623                 const qreal scale = painter->transform().m22();
  7715                 const qreal scale = painter->transform().m22();
  7624                 if (scale != 0)
  7716                 if (scale != 0)
  7625                     yoff = qRound(yoff * scale) / scale;
  7717                     yoff = -qRound(-yoff * scale) / scale;
  7626             }
  7718             }
  7627         }
  7719         }
  7628     }
  7720     }
  7629     if (tf & Qt::AlignRight) {
  7721     if (tf & Qt::AlignRight) {
  7630         xoff = r.width() - width;
  7722         xoff = r.width() - width;