# qwt_painter.cpp

    00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
    00002  * Qwt Widget Library
    00003  * Copyright (C) 1997   Josef Wilgen
    00004  * Copyright (C) 2002   Uwe Rathmann
    00005  *
    00006  * This library is free software; you can redistribute it and/or
    00007  * modify it under the terms of the Qwt License, Version 1.0
    00008  *****************************************************************************/
    00009
    00010 // vim: expandtab
    00011
    00012 #include <qwindowdefs.h>
    00013 #include <qwidget.h>
    00014 #include <qrect.h>
    00015 #include <qpainter.h>
    00016 #include <qpalette.h>
    00017 #include <qpaintdevice.h>
    00018 #include <qpixmap.h>
    00019 #include <qstyle.h>
    00020 #if QT_VERSION < 0x040000
    00021 #include <qsimplerichtext.h>
    00022 #else
    00023 #include <qtextdocument.h>
    00024 #include <qabstracttextdocumentlayout.h>
    00025 #include <qstyleoption.h>
    00026 #include <qpaintengine.h>
    00027 #endif
    00028
    00029 #include "qwt_clipper.h"
    00030 #include "qwt_math.h"
    00031 #include "qwt_color_map.h"
    00032 #include "qwt_scale_map.h"
    00033 #include "qwt_painter.h"
    00034
    00035 QwtMetricsMap QwtPainter::d_metricsMap;
    00036
    00037 #if defined(Q_WS_X11)
    00038 bool QwtPainter::d_deviceClipping = true;
    00039 #else
    00040 bool QwtPainter::d_deviceClipping = false;
    00041 #endif
    00042
    00043 #if QT_VERSION < 0x040000
    00044 bool QwtPainter::d_SVGMode = false;
    00045 #endif
    00046
    00047 static inline bool needDeviceClipping(
    00048     const QPainter *painter, bool deviceClipping)
    00049 {
    00050     return deviceClipping &&
    00051         (painter->device()->devType() == QInternal::Widget ||
    00052           painter->device()->devType() == QInternal::Pixmap );
    00053 }
    00054
    00062 void QwtPainter::setDeviceClipping(bool enable)
    00063 {
    00064     d_deviceClipping = enable;
    00065 }
    00066
    00073 bool QwtPainter::deviceClipping()
    00074 {
    00075     return d_deviceClipping;
    00076 }
    00077
    00082 const QRect &QwtPainter::deviceClipRect()
    00083 {
    00084     static QRect clip;
    00085
    00086     if ( !clip.isValid() )
    00087     {
    00088         clip.setCoords(QWT_COORD_MIN, QWT_COORD_MIN,
    00089             QWT_COORD_MAX, QWT_COORD_MAX);
    00090     }
    00091     return clip;
    00092 }
    00093
    00095 QwtPolygon QwtPainter::clip(const QwtPolygon &pa)
    00096 {
    00097     return QwtClipper::clipPolygon(deviceClipRect(), pa);
    00098 }
    00099
    00100 #if QT_VERSION < 0x040000
    00101
    00113 void QwtPainter::setSVGMode(bool on)
    00114 {
    00115     d_SVGMode = on;
    00116 }
    00117
    00118 bool QwtPainter::isSVGMode()
    00119 {
    00120     return d_SVGMode;
    00121 }
    00122
    00123 #endif // QT_VERSION < 0x040000
    00124
    00133 void QwtPainter::setMetricsMap(const QPaintDevice *layout,
    00134     const QPaintDevice *device)
    00135 {
    00136     d_metricsMap.setMetrics(layout, device);
    00137 }
    00138
    00143 void QwtPainter::setMetricsMap(const QwtMetricsMap &map)
    00144 {
    00145     d_metricsMap = map;
    00146 }
    00147
    00152 void QwtPainter::resetMetricsMap()
    00153 {
    00154     d_metricsMap = QwtMetricsMap();
    00155 }
    00156
    00160 const QwtMetricsMap &QwtPainter::metricsMap()
    00161 {
    00162     return d_metricsMap;
    00163 }
    00164
    00168 void QwtPainter::setClipRect(QPainter *painter, const QRect &rect)
    00169 {
    00170     painter->setClipRect(d_metricsMap.layoutToDevice(rect, painter));
    00171 }
    00172
    00176 void QwtPainter::drawRect(QPainter *painter, int x, int y, int w, int h)
    00177 {
    00178     drawRect(painter, QRect(x, y, w, h));
    00179 }
    00180
    00184 void QwtPainter::drawRect(QPainter *painter, const QRect &rect)
    00185 {
    00186     const QRect r = d_metricsMap.layoutToDevice(rect, painter);
    00187
    00188     QRect clipRect;
    00189
    00190     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00191     if ( deviceClipping )
    00192         clipRect = deviceClipRect();
    00193
    00194     if ( clipRect.isValid() )
    00195     {
    00196         if ( !clipRect.intersects(r) )
    00197             return;
    00198
    00199         if ( !clipRect.contains(r) )
    00200         {
    00201             fillRect(painter, r & clipRect, painter->brush());
    00202
    00203             int pw = painter->pen().width();
    00204             pw = pw % 2 + pw / 2;
    00205
    00206             QwtPolygon pa(5);
    00207             pa.setPoint(0, r.left(), r.top());
    00208             pa.setPoint(1, r.right() - pw, r.top());
    00209             pa.setPoint(2, r.right() - pw, r.bottom() - pw);
    00210             pa.setPoint(3, r.left(), r.bottom() - pw);
    00211             pa.setPoint(4, r.left(), r.top());
    00212
    00213             painter->save();
    00214             painter->setBrush(Qt::NoBrush);
    00215             drawPolyline(painter, pa);
    00216             painter->restore();
    00217
    00218             return;
    00219         }
    00220     }
    00221
    00222     painter->drawRect(r);
    00223 }
    00224
    00228 void QwtPainter::fillRect(QPainter *painter,
    00229     const QRect &rect, const QBrush &brush)
    00230 {
    00231     if ( !rect.isValid() )
    00232         return;
    00233
    00234     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00235
    00236     QRect clipRect;
    00237 #if QT_VERSION >= 0x040000
    00238
    00239     /*
    00240       Performance of Qt4 is horrible for non trivial brushs. Without
    00241       clipping expect minutes or hours for repainting large rects
    00242       (might result from zooming)
    00243     */
    00244
    00245     clipRect = painter->window();
    00246     if ( painter->hasClipping() )
    00247         clipRect &= painter->clipRegion().boundingRect();
    00248     if ( deviceClipping )
    00249         clipRect &= deviceClipRect();
    00250 #else
    00251     if ( deviceClipping )
    00252         clipRect = deviceClipRect();
    00253 #endif
    00254
    00255     QRect r = d_metricsMap.layoutToDevice(rect, painter);
    00256     if ( clipRect.isValid() )
    00257         r = r.intersect(clipRect);
    00258
    00259     if ( r.isValid() )
    00260         painter->fillRect(r, brush);
    00261 }
    00262
    00266 void QwtPainter::drawPie(QPainter *painter, const QRect &rect,
    00267     int a, int alen)
    00268 {
    00269     QRect r = d_metricsMap.layoutToDevice(rect, painter);
    00270
    00271     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00272     if ( deviceClipping && !deviceClipRect().contains(rect) )
    00273         return;
    00274
    00275     painter->drawPie(r, a, alen);
    00276 }
    00277
    00281 void QwtPainter::drawEllipse(QPainter *painter, const QRect &rect)
    00282 {
    00283     QRect r = d_metricsMap.layoutToDevice(rect, painter);
    00284
    00285     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00286
    00287     if ( deviceClipping && !deviceClipRect().contains(rect) )
    00288         return;
    00289
    00290 #if QT_VERSION >= 0x040000
    00291     if ( painter->pen().style() != Qt::NoPen &&
    00292         painter->pen().color().isValid() )
    00293     {
    00294         // Qt4 adds the pen to the rect, Qt3 not.
    00295         int pw = painter->pen().width();
    00296         if ( pw == 0 )
    00297             pw = 1;
    00298
    00299         r.setWidth(r.width() - pw);
    00300         r.setHeight(r.height() - pw);
    00301     }
    00302 #endif
    00303
    00304     painter->drawEllipse(r);
    00305 }
    00306
    00310 void QwtPainter::drawText(QPainter *painter, int x, int y,
    00311         const QString &text)
    00312 {
    00313     drawText(painter, QPoint(x, y), text);
    00314 }
    00315
    00319 void QwtPainter::drawText(QPainter *painter, const QPoint &pos,
    00320         const QString &text)
    00321 {
    00322     const QPoint p = d_metricsMap.layoutToDevice(pos, painter);
    00323
    00324     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00325
    00326     if ( deviceClipping && !deviceClipRect().contains(p) )
    00327         return;
    00328
    00329     painter->drawText(p, text);
    00330 }
    00331
    00335 void QwtPainter::drawText(QPainter *painter, int x, int y, int w, int h,
    00336         int flags, const QString &text)
    00337 {
    00338     drawText(painter, QRect(x, y, w, h), flags, text);
    00339 }
    00340
    00344 void QwtPainter::drawText(QPainter *painter, const QRect &rect,
    00345         int flags, const QString &text)
    00346 {
    00347     QRect textRect = d_metricsMap.layoutToDevice(rect, painter);
    00348 #if QT_VERSION < 0x040000
    00349     if ( d_SVGMode &&
    00350         ( flags == 0 || flags & Qt::AlignVCenter )
    00351         && painter->device()->devType() & QInternal::Picture )
    00352     {
    00353         /*
    00354             Qt3 misalignes texts, when saving a text
    00355             to a SVG image.
    00356          */
    00357         textRect.setY(textRect.y() - painter->fontMetrics().height() / 4);
    00358     }
    00359 #endif
    00360     painter->drawText(textRect, flags, text);
    00361 }
    00362
    00363 #ifndef QT_NO_RICHTEXT
    00364
    00368 #if QT_VERSION < 0x040000
    00369
    00370 void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect,
    00371     int flags, QSimpleRichText &text)
    00372 {
    00373     QColorGroup cg;
    00374     cg.setColor(QColorGroup::Text, painter->pen().color());
    00375
    00376     const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter);
    00377
    00378     text.setWidth(painter, scaledRect.width());
    00379
    00380     // QSimpleRichText is Qt::AlignTop by default
    00381
    00382     int y = scaledRect.y();
    00383     if (flags & Qt::AlignBottom)
    00384         y += (scaledRect.height() - text.height());
    00385     else if (flags & Qt::AlignVCenter)
    00386         y += (scaledRect.height() - text.height())/2;
    00387
    00388     text.draw(painter, scaledRect.x(), y, scaledRect, cg);
    00389 }
    00390 #else
    00391 void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect,
    00392     int flags, QTextDocument &text)
    00393 {
    00394     const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter);
    00395     text.setPageSize(QSize(scaledRect.width(), QWIDGETSIZE_MAX));
    00396
    00397     QAbstractTextDocumentLayout* layout = text.documentLayout();
    00398
    00399     const int height = qRound(layout->documentSize().height());
    00400     int y = scaledRect.y();
    00401     if (flags & Qt::AlignBottom)
    00402         y += (scaledRect.height() - height);
    00403     else if (flags & Qt::AlignVCenter)
    00404         y += (scaledRect.height() - height)/2;
    00405
    00406     QAbstractTextDocumentLayout::PaintContext context;
    00407     context.palette.setColor(QPalette::Text, painter->pen().color());
    00408
    00409     painter->save();
    00410
    00411     painter->translate(scaledRect.x(), y);
    00412     layout->draw(painter, context);
    00413
    00414     painter->restore();
    00415 }
    00416 #endif
    00417
    00418 #endif // !QT_NO_RICHTEXT
    00419
    00420
    00424 void QwtPainter::drawLine(QPainter *painter, int x1, int y1, int x2, int y2)
    00425 {
    00426     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00427
    00428     if ( deviceClipping &&
    00429         !(deviceClipRect().contains(x1, y1) && deviceClipRect().contains(x2, y2)) )
    00430     {
    00431         QwtPolygon pa(2);
    00432         pa.setPoint(0, x1, y1);
    00433         pa.setPoint(1, x2, y2);
    00434         drawPolyline(painter, pa);
    00435         return;
    00436     }
    00437
    00438     if ( d_metricsMap.isIdentity() )
    00439     {
    00440 #if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000
    00441         if ( !painter->device()->isExtDev() )
    00442 #endif
    00443         {
    00444             painter->drawLine(x1, y1, x2, y2);
    00445             return;
    00446         }
    00447     }
    00448
    00449     const QPoint p1 = d_metricsMap.layoutToDevice(QPoint(x1, y1));
    00450     const QPoint p2 = d_metricsMap.layoutToDevice(QPoint(x2, y2));
    00451
    00452 #if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000
    00453     if ( painter->device()->isExtDev() )
    00454     {
    00455         // Strange: the postscript driver of QPrinter adds an offset
    00456         // of 0.5 to the start/endpoint when using drawLine, but not
    00457         // for lines painted with drawLineSegments.
    00458
    00459         QwtPolygon pa(2);
    00460         pa.setPoint(0, p1);
    00461         pa.setPoint(1, p2);
    00462         painter->drawLineSegments(pa);
    00463     }
    00464     else
    00465         painter->drawLine(p1, p2);
    00466 #else
    00467     painter->drawLine(p1, p2);
    00468 #endif
    00469 }
    00470
    00474 void QwtPainter::drawPolygon(QPainter *painter, const QwtPolygon &pa)
    00475 {
    00476     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00477
    00478     QwtPolygon cpa = d_metricsMap.layoutToDevice(pa);
    00479     if ( deviceClipping )
    00480     {
    00481 #ifdef __GNUC__
    00482 #endif
    00483         cpa = clip(cpa);
    00484     }
    00485     painter->drawPolygon(cpa);
    00486 }
    00487
    00491 void QwtPainter::drawPolyline(QPainter *painter, const QwtPolygon &pa)
    00492 {
    00493     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00494
    00495     QwtPolygon cpa = d_metricsMap.layoutToDevice(pa);
    00496     if ( deviceClipping )
    00497         cpa = clip(cpa);
    00498
    00499 #if QT_VERSION >= 0x040000
    00500     bool doSplit = false;
    00501     if ( painter->paintEngine()->type() == QPaintEngine::Raster &&
    00502         painter->pen().width() >= 2 )
    00503     {
    00504         /*
    00505             The raster paint engine seems to use some algo with O(n*n).
    00506             ( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
    00507             To work around this problem, we have to split the polygon into
    00508             smaller pieces.
    00509          */
    00510         doSplit = true;
    00511     }
    00512
    00513     if ( doSplit )
    00514     {
    00515         const int numPoints = cpa.size();
    00516         const QPoint *points = cpa.data();
    00517
    00518         const int splitSize = 20;
    00519         for ( int i = 0; i < numPoints; i += splitSize )
    00520         {
    00521             const int n = qwtMin(splitSize + 1, cpa.size() - i);
    00522             painter->drawPolyline(points + i, n);
    00523         }
    00524     }
    00525     else
    00526 #endif
    00527         painter->drawPolyline(cpa);
    00528 }
    00529
    00534 void QwtPainter::drawPoint(QPainter *painter, int x, int y)
    00535 {
    00536     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00537
    00538     const QPoint pos = d_metricsMap.layoutToDevice(QPoint(x, y));
    00539
    00540     if ( deviceClipping && !deviceClipRect().contains(pos) )
    00541         return;
    00542
    00543     painter->drawPoint(pos);
    00544 }
    00545
    00546 void QwtPainter::drawColoredArc(QPainter *painter, const QRect &rect,
    00547     int peak, int arc, int interval, const QColor &c1, const QColor &c2)
    00548 {
    00549     int h1, s1, v1;
    00550     int h2, s2, v2;
    00551
    00552 #if QT_VERSION < 0x040000
    00553     c1.hsv(&h1, &s1, &v1);
    00554     c2.hsv(&h2, &s2, &v2);
    00555 #else
    00556     c1.getHsv(&h1, &s1, &v1);
    00557     c2.getHsv(&h2, &s2, &v2);
    00558 #endif
    00559
    00560     arc /= 2;
    00561     for ( int angle = -arc; angle < arc; angle += interval)
    00562     {
    00563         double ratio;
    00564         if ( angle >= 0 )
    00565             ratio = 1.0 - angle / double(arc);
    00566         else
    00567             ratio = 1.0 + angle / double(arc);
    00568
    00569
    00570         QColor c;
    00571         c.setHsv( h1 + qRound(ratio * (h2 - h1)),
    00572             s1 + qRound(ratio * (s2 - s1)),
    00573             v1 + qRound(ratio * (v2 - v1)) );
    00574
    00575         painter->setPen(QPen(c, painter->pen().width()));
    00576         painter->drawArc(rect, (peak + angle) * 16, interval * 16);
    00577     }
    00578 }
    00579
    00580 void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget)
    00581 {
    00582     drawFocusRect(painter, widget, widget->rect());
    00583 }
    00584
    00585 void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget,
    00586     const QRect &rect)
    00587 {
    00588 #if QT_VERSION < 0x040000
    00589         widget->style().drawPrimitive(QStyle::PE_FocusRect, painter,
    00590             rect, widget->colorGroup());
    00591 #else
    00592         QStyleOptionFocusRect opt;
    00593         opt.init(widget);
    00594         opt.rect = rect;
    00595         opt.state |= QStyle::State_HasFocus;
    00596
    00597         widget->style()->drawPrimitive(QStyle::PE_FrameFocusRect,
    00598             &opt, painter, widget);
    00599 #endif
    00600
    00601 }
    00602
    00604 #if QT_VERSION < 0x040000
    00605 void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect,
    00606     int width, const QColorGroup &cg, bool sunken)
    00607 #else
    00608 void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect,
    00609     int width, const QPalette &palette, bool sunken)
    00610 #endif
    00611 {
    00612
    00613 #if QT_VERSION < 0x040000
    00614     QColor c0 = cg.mid();
    00615     QColor c1, c2;
    00616     if ( sunken )
    00617     {
    00618         c1 = cg.dark();
    00619         c2 = cg.light();
    00620     }
    00621     else
    00622     {
    00623         c1 = cg.light();
    00624         c2 = cg.dark();
    00625     }
    00626 #else
    00627     QColor c0 = palette.color(QPalette::Mid);
    00628     QColor c1, c2;
    00629     if ( sunken )
    00630     {
    00631         c1 = palette.color(QPalette::Dark);
    00632         c2 = palette.color(QPalette::Light);
    00633     }
    00634     else
    00635     {
    00636         c1 = palette.color(QPalette::Light);
    00637         c2 = palette.color(QPalette::Dark);
    00638     }
    00639 #endif
    00640
    00641     painter->setPen(QPen(c0, width));
    00642     painter->drawArc(rect, 0, 360 * 16); // full
    00643
    00644     const int peak = 150;
    00645     const int interval = 2;
    00646
    00647     if ( c0 != c1 )
    00648         drawColoredArc(painter, rect, peak, 160, interval, c0, c1);
    00649     if ( c0 != c2 )
    00650         drawColoredArc(painter, rect, peak + 180, 120, interval, c0, c2);
    00651 }
    00652
    00653 void QwtPainter::drawColorBar(QPainter *painter,
    00654         const QwtColorMap &colorMap, const QwtDoubleInterval &interval,
    00655         const QwtScaleMap &scaleMap, Qt::Orientation orientation,
    00656         const QRect &rect)
    00657 {
    00658 #if QT_VERSION < 0x040000
    00659     QValueVector<QRgb> colorTable;
    00660 #else
    00661     QVector<QRgb> colorTable;
    00662 #endif
    00663     if ( colorMap.format() == QwtColorMap::Indexed )
    00664         colorTable = colorMap.colorTable(interval);
    00665
    00666     QColor c;
    00667
    00668     const QRect devRect = d_metricsMap.layoutToDevice(rect);
    00669
    00670     /*
    00671       We paint to a pixmap first to have something scalable for printing
    00672       ( f.e. in a Pdf document )
    00673      */
    00674
    00675     QPixmap pixmap(devRect.size());
    00676     QPainter pmPainter(&pixmap);
    00677     pmPainter.translate(-devRect.x(), -devRect.y());
    00678
    00679     if ( orientation == Qt::Horizontal )
    00680     {
    00681         QwtScaleMap sMap = scaleMap;
    00682         sMap.setPaintInterval(devRect.left(), devRect.right());
    00683
    00684         for ( int x = devRect.left(); x <= devRect.right(); x++ )
    00685         {
    00686             const double value = sMap.invTransform(x);
    00687
    00688             if ( colorMap.format() == QwtColorMap::RGB )
    00689                 c.setRgb(colorMap.rgb(interval, value));
    00690             else
    00691                 c = colorTable[colorMap.colorIndex(interval, value)];
    00692
    00693             pmPainter.setPen(c);
    00694             pmPainter.drawLine(x, devRect.top(), x, devRect.bottom());
    00695         }
    00696     }
    00697     else // Vertical
    00698     {
    00699         QwtScaleMap sMap = scaleMap;
    00700         sMap.setPaintInterval(devRect.bottom(), devRect.top());
    00701
    00702         for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
    00703         {
    00704             const double value = sMap.invTransform(y);
    00705
    00706             if ( colorMap.format() == QwtColorMap::RGB )
    00707                 c.setRgb(colorMap.rgb(interval, value));
    00708             else
    00709                 c = colorTable[colorMap.colorIndex(interval, value)];
    00710
    00711             pmPainter.setPen(c);
    00712             pmPainter.drawLine(devRect.left(), y, devRect.right(), y);
    00713         }
    00714     }
    00715     pmPainter.end();
    00716     painter->drawPixmap(devRect, pixmap);
    00717 }