# 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_rect.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     const QwtRect rect(deviceClipRect());
    00098     return rect.clip(pa);
    00099 }
    00100
    00101 #if QT_VERSION < 0x040000
    00102
    00114 void QwtPainter::setSVGMode(bool on)
    00115 {
    00116     d_SVGMode = on;
    00117 }
    00118
    00119 bool QwtPainter::isSVGMode()
    00120 {
    00121     return d_SVGMode;
    00122 }
    00123
    00124 #endif // QT_VERSION < 0x040000
    00125
    00134 void QwtPainter::setMetricsMap(const QPaintDevice *layout,
    00135     const QPaintDevice *device)
    00136 {
    00137     d_metricsMap.setMetrics(layout, device);
    00138 }
    00139
    00144 void QwtPainter::setMetricsMap(const QwtMetricsMap &map)
    00145 {
    00146     d_metricsMap = map;
    00147 }
    00148
    00153 void QwtPainter::resetMetricsMap()
    00154 {
    00155     d_metricsMap = QwtMetricsMap();
    00156 }
    00157
    00161 const QwtMetricsMap &QwtPainter::metricsMap()
    00162 {
    00163     return d_metricsMap;
    00164 }
    00165
    00169 void QwtPainter::setClipRect(QPainter *painter, const QRect &rect)
    00170 {
    00171     painter->setClipRect(d_metricsMap.layoutToDevice(rect, painter));
    00172 }
    00173
    00177 void QwtPainter::drawRect(QPainter *painter, int x, int y, int w, int h)
    00178 {
    00179     drawRect(painter, QRect(x, y, w, h));
    00180 }
    00181
    00185 void QwtPainter::drawRect(QPainter *painter, const QRect &rect)
    00186 {
    00187     QRect r = d_metricsMap.layoutToDevice(rect, painter);
    00188
    00189     QRect clipRect;
    00190
    00191     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00192     if ( deviceClipping )
    00193         clipRect = deviceClipRect();
    00194
    00195     if ( clipRect.isValid() )
    00196     {
    00197         if ( !clipRect.intersects(r) )
    00198             return;
    00199
    00200         if ( !clipRect.contains(r) )
    00201         {
    00202             fillRect(painter, r & clipRect, painter->brush());
    00203
    00204             int pw = painter->pen().width();
    00205             pw = pw % 2 + pw / 2;
    00206
    00207             QwtPolygon pa(5);
    00208             pa.setPoint(0, r.left(), r.top());
    00209             pa.setPoint(1, r.right() - pw, r.top());
    00210             pa.setPoint(2, r.right() - pw, r.bottom() - pw);
    00211             pa.setPoint(3, r.left(), r.bottom() - pw);
    00212             pa.setPoint(4, r.left(), r.top());
    00213
    00214             painter->save();
    00215             painter->setBrush(Qt::NoBrush);
    00216             drawPolyline(painter, pa);
    00217             painter->restore();
    00218
    00219             return;
    00220         }
    00221     }
    00222
    00223 #if QT_VERSION >= 0x040000
    00224     if ( painter->pen().style() != Qt::NoPen &&
    00225         painter->pen().color().isValid() )
    00226     {
    00227         // Qt4 adds the pen to the rect, Qt3 not.
    00228         int pw = painter->pen().width();
    00229         if ( pw == 0 )
    00230             pw = 1;
    00231
    00232         r.setWidth(r.width() - pw);
    00233         r.setHeight(r.height() - pw);
    00234     }
    00235 #endif
    00236     painter->drawRect(r);
    00237 }
    00238
    00242 void QwtPainter::fillRect(QPainter *painter,
    00243     const QRect &rect, const QBrush &brush)
    00244 {
    00245     if ( !rect.isValid() )
    00246         return;
    00247
    00248     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00249
    00250     QRect clipRect;
    00251 #if QT_VERSION >= 0x040000
    00252
    00253     /*
    00254       Performance of Qt4 is horrible for non trivial brushs. Without
    00255       clipping expect minutes or hours for repainting large rects
    00256       (might result from zooming)
    00257     */
    00258
    00259     clipRect = painter->window();
    00260     if ( painter->hasClipping() )
    00261         clipRect &= painter->clipRegion().boundingRect();
    00262     if ( deviceClipping )
    00263         clipRect &= deviceClipRect();
    00264 #else
    00265     if ( deviceClipping )
    00266         clipRect = deviceClipRect();
    00267 #endif
    00268
    00269     QRect r = d_metricsMap.layoutToDevice(rect, painter);
    00270     if ( clipRect.isValid() )
    00271         r = r.intersect(clipRect);
    00272
    00273     if ( r.isValid() )
    00274         painter->fillRect(r, brush);
    00275 }
    00276
    00280 void QwtPainter::drawPie(QPainter *painter, const QRect &rect,
    00281     int a, int alen)
    00282 {
    00283     QRect r = d_metricsMap.layoutToDevice(rect, painter);
    00284
    00285     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00286     if ( deviceClipping && !deviceClipRect().contains(rect) )
    00287         return;
    00288
    00289     painter->drawPie(r, a, alen);
    00290 }
    00291
    00295 void QwtPainter::drawEllipse(QPainter *painter, const QRect &rect)
    00296 {
    00297     QRect r = d_metricsMap.layoutToDevice(rect, painter);
    00298
    00299     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00300
    00301     if ( deviceClipping && !deviceClipRect().contains(rect) )
    00302         return;
    00303
    00304 #if QT_VERSION >= 0x040000
    00305     if ( painter->pen().style() != Qt::NoPen &&
    00306         painter->pen().color().isValid() )
    00307     {
    00308         // Qt4 adds the pen to the rect, Qt3 not.
    00309         int pw = painter->pen().width();
    00310         if ( pw == 0 )
    00311             pw = 1;
    00312
    00313         r.setWidth(r.width() - pw);
    00314         r.setHeight(r.height() - pw);
    00315     }
    00316 #endif
    00317
    00318     painter->drawEllipse(r);
    00319 }
    00320
    00324 void QwtPainter::drawText(QPainter *painter, int x, int y,
    00325         const QString &text)
    00326 {
    00327     drawText(painter, QPoint(x, y), text);
    00328 }
    00329
    00333 void QwtPainter::drawText(QPainter *painter, const QPoint &pos,
    00334         const QString &text)
    00335 {
    00336     const QPoint p = d_metricsMap.layoutToDevice(pos, painter);
    00337
    00338     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00339
    00340     if ( deviceClipping && !deviceClipRect().contains(p) )
    00341         return;
    00342
    00343     painter->drawText(p, text);
    00344 }
    00345
    00349 void QwtPainter::drawText(QPainter *painter, int x, int y, int w, int h,
    00350         int flags, const QString &text)
    00351 {
    00352     drawText(painter, QRect(x, y, w, h), flags, text);
    00353 }
    00354
    00358 void QwtPainter::drawText(QPainter *painter, const QRect &rect,
    00359         int flags, const QString &text)
    00360 {
    00361     QRect textRect = d_metricsMap.layoutToDevice(rect, painter);
    00362 #if QT_VERSION < 0x040000
    00363     if ( d_SVGMode &&
    00364         ( flags == 0 || flags & Qt::AlignVCenter )
    00365         && painter->device()->devType() & QInternal::Picture )
    00366     {
    00367         /*
    00368             Qt3 misalignes texts, when saving a text
    00369             to a SVG image.
    00370          */
    00371         textRect.setY(textRect.y() - painter->fontMetrics().height() / 4);
    00372     }
    00373 #endif
    00374     painter->drawText(textRect, flags, text);
    00375 }
    00376
    00377 #ifndef QT_NO_RICHTEXT
    00378
    00382 #if QT_VERSION < 0x040000
    00383
    00384 void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect,
    00385     int flags, QSimpleRichText &text)
    00386 {
    00387     QColorGroup cg;
    00388     cg.setColor(QColorGroup::Text, painter->pen().color());
    00389
    00390     const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter);
    00391
    00392     text.setWidth(painter, scaledRect.width());
    00393
    00394     // QSimpleRichText is Qt::AlignTop by default
    00395
    00396     int y = scaledRect.y();
    00397     if (flags & Qt::AlignBottom)
    00398         y += (scaledRect.height() - text.height());
    00399     else if (flags & Qt::AlignVCenter)
    00400         y += (scaledRect.height() - text.height())/2;
    00401
    00402     text.draw(painter, scaledRect.x(), y, scaledRect, cg);
    00403 }
    00404 #else
    00405 void QwtPainter::drawSimpleRichText(QPainter *painter, const QRect &rect,
    00406     int flags, QTextDocument &text)
    00407 {
    00408     const QRect scaledRect = d_metricsMap.layoutToDevice(rect, painter);
    00409     text.setPageSize(QSize(scaledRect.width(), QWIDGETSIZE_MAX));
    00410
    00411     QAbstractTextDocumentLayout* layout = text.documentLayout();
    00412
    00413     const int height = qRound(layout->documentSize().height());
    00414     int y = scaledRect.y();
    00415     if (flags & Qt::AlignBottom)
    00416         y += (scaledRect.height() - height);
    00417     else if (flags & Qt::AlignVCenter)
    00418         y += (scaledRect.height() - height)/2;
    00419
    00420     QAbstractTextDocumentLayout::PaintContext context;
    00421     context.palette.setColor(QPalette::Text, painter->pen().color());
    00422
    00423     painter->save();
    00424
    00425     painter->translate(scaledRect.x(), y);
    00426     layout->draw(painter, context);
    00427
    00428     painter->restore();
    00429 }
    00430 #endif
    00431
    00432 #endif // !QT_NO_RICHTEXT
    00433
    00434
    00438 void QwtPainter::drawLine(QPainter *painter, int x1, int y1, int x2, int y2)
    00439 {
    00440     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00441
    00442     if ( deviceClipping &&
    00443         !(deviceClipRect().contains(x1, y1) && deviceClipRect().contains(x2, y2)) )
    00444     {
    00445         QwtPolygon pa(2);
    00446         pa.setPoint(0, x1, y1);
    00447         pa.setPoint(1, x2, y2);
    00448         drawPolyline(painter, pa);
    00449         return;
    00450     }
    00451
    00452     if ( d_metricsMap.isIdentity() )
    00453     {
    00454 #if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000
    00455         if ( !painter->device()->isExtDev() )
    00456 #endif
    00457         {
    00458             painter->drawLine(x1, y1, x2, y2);
    00459             return;
    00460         }
    00461     }
    00462
    00463     const QPoint p1 = d_metricsMap.layoutToDevice(QPoint(x1, y1));
    00464     const QPoint p2 = d_metricsMap.layoutToDevice(QPoint(x2, y2));
    00465
    00466 #if QT_VERSION >= 0x030200 && QT_VERSION < 0x040000
    00467     if ( painter->device()->isExtDev() )
    00468     {
    00469         // Strange: the postscript driver of QPrinter adds an offset
    00470         // of 0.5 to the start/endpoint when using drawLine, but not
    00471         // for lines painted with drawLineSegments.
    00472
    00473         QwtPolygon pa(2);
    00474         pa.setPoint(0, p1);
    00475         pa.setPoint(1, p2);
    00476         painter->drawLineSegments(pa);
    00477     }
    00478     else
    00479         painter->drawLine(p1, p2);
    00480 #else
    00481     painter->drawLine(p1, p2);
    00482 #endif
    00483 }
    00484
    00488 void QwtPainter::drawPolygon(QPainter *painter, const QwtPolygon &pa)
    00489 {
    00490     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00491
    00492     QwtPolygon cpa = d_metricsMap.layoutToDevice(pa);
    00493     if ( deviceClipping )
    00494     {
    00495 #ifdef __GNUC__
    00496 #endif
    00497         cpa = clip(cpa);
    00498     }
    00499     painter->drawPolygon(cpa);
    00500 }
    00501
    00505 void QwtPainter::drawPolyline(QPainter *painter, const QwtPolygon &pa)
    00506 {
    00507     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00508
    00509     QwtPolygon cpa = d_metricsMap.layoutToDevice(pa);
    00510     if ( deviceClipping )
    00511         cpa = clip(cpa);
    00512
    00513 #if QT_VERSION >= 0x040000
    00514     bool doSplit = false;
    00515     if ( painter->paintEngine()->type() == QPaintEngine::Raster &&
    00516         painter->pen().width() >= 2 )
    00517     {
    00518         /*
    00519             The raster paint engine seems to use some algo with O(n*n).
    00520             ( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
    00521             To work around this problem, we have to split the polygon into
    00522             smaller pieces.
    00523          */
    00524         doSplit = true;
    00525     }
    00526
    00527     if ( doSplit )
    00528     {
    00529         const int numPoints = cpa.size();
    00530         const QPoint *points = cpa.data();
    00531
    00532         const int splitSize = 20;
    00533         for ( int i = 0; i < numPoints; i += splitSize )
    00534         {
    00535             const int n = qwtMin(splitSize + 1, cpa.size() - i);
    00536             painter->drawPolyline(points + i, n);
    00537         }
    00538     }
    00539     else
    00540 #endif
    00541         painter->drawPolyline(cpa);
    00542 }
    00543
    00548 void QwtPainter::drawPoint(QPainter *painter, int x, int y)
    00549 {
    00550     const bool deviceClipping = needDeviceClipping(painter, d_deviceClipping);
    00551
    00552     const QPoint pos = d_metricsMap.layoutToDevice(QPoint(x, y));
    00553
    00554     if ( deviceClipping && !deviceClipRect().contains(pos) )
    00555         return;
    00556
    00557     painter->drawPoint(pos);
    00558 }
    00559
    00560 void QwtPainter::drawColoredArc(QPainter *painter, const QRect &rect,
    00561     int peak, int arc, int interval, const QColor &c1, const QColor &c2)
    00562 {
    00563     int h1, s1, v1;
    00564     int h2, s2, v2;
    00565
    00566 #if QT_VERSION < 0x040000
    00567     c1.hsv(&h1, &s1, &v1);
    00568     c2.hsv(&h2, &s2, &v2);
    00569 #else
    00570     c1.getHsv(&h1, &s1, &v1);
    00571     c2.getHsv(&h2, &s2, &v2);
    00572 #endif
    00573
    00574     arc /= 2;
    00575     for ( int angle = -arc; angle < arc; angle += interval)
    00576     {
    00577         double ratio;
    00578         if ( angle >= 0 )
    00579             ratio = 1.0 - angle / double(arc);
    00580         else
    00581             ratio = 1.0 + angle / double(arc);
    00582
    00583
    00584         QColor c;
    00585         c.setHsv( h1 + qRound(ratio * (h2 - h1)),
    00586             s1 + qRound(ratio * (s2 - s1)),
    00587             v1 + qRound(ratio * (v2 - v1)) );
    00588
    00589         painter->setPen(QPen(c, painter->pen().width()));
    00590         painter->drawArc(rect, (peak + angle) * 16, interval * 16);
    00591     }
    00592 }
    00593
    00594 void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget)
    00595 {
    00596     drawFocusRect(painter, widget, widget->rect());
    00597 }
    00598
    00599 void QwtPainter::drawFocusRect(QPainter *painter, QWidget *widget,
    00600     const QRect &rect)
    00601 {
    00602 #if QT_VERSION < 0x040000
    00603         widget->style().drawPrimitive(QStyle::PE_FocusRect, painter,
    00604             rect, widget->colorGroup());
    00605 #else
    00606         QStyleOptionFocusRect opt;
    00607         opt.init(widget);
    00608         opt.rect = rect;
    00609         opt.state |= QStyle::State_HasFocus;
    00610
    00611         widget->style()->drawPrimitive(QStyle::PE_FrameFocusRect,
    00612             &opt, painter, widget);
    00613 #endif
    00614
    00615 }
    00616
    00618 #if QT_VERSION < 0x040000
    00619 void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect,
    00620     int width, const QColorGroup &cg, bool sunken)
    00621 #else
    00622 void QwtPainter::drawRoundFrame(QPainter *painter, const QRect &rect,
    00623     int width, const QPalette &palette, bool sunken)
    00624 #endif
    00625 {
    00626
    00627 #if QT_VERSION < 0x040000
    00628     QColor c0 = cg.mid();
    00629     QColor c1, c2;
    00630     if ( sunken )
    00631     {
    00632         c1 = cg.dark();
    00633         c2 = cg.light();
    00634     }
    00635     else
    00636     {
    00637         c1 = cg.light();
    00638         c2 = cg.dark();
    00639     }
    00640 #else
    00641     QColor c0 = palette.color(QPalette::Mid);
    00642     QColor c1, c2;
    00643     if ( sunken )
    00644     {
    00645         c1 = palette.color(QPalette::Dark);
    00646         c2 = palette.color(QPalette::Light);
    00647     }
    00648     else
    00649     {
    00650         c1 = palette.color(QPalette::Light);
    00651         c2 = palette.color(QPalette::Dark);
    00652     }
    00653 #endif
    00654
    00655     painter->setPen(QPen(c0, width));
    00656     painter->drawArc(rect, 0, 360 * 16); // full
    00657
    00658     const int peak = 150;
    00659     const int interval = 2;
    00660
    00661     if ( c0 != c1 )
    00662         drawColoredArc(painter, rect, peak, 160, interval, c0, c1);
    00663     if ( c0 != c2 )
    00664         drawColoredArc(painter, rect, peak + 180, 120, interval, c0, c2);
    00665 }
    00666
    00667 void QwtPainter::drawColorBar(QPainter *painter,
    00668         const QwtColorMap &colorMap, const QwtDoubleInterval &interval,
    00669         const QwtScaleMap &scaleMap, Qt::Orientation orientation,
    00670         const QRect &rect)
    00671 {
    00672     painter->save();
    00673
    00674     QwtPainter::setClipRect(painter, rect);
    00675
    00676 #if QT_VERSION < 0x040000
    00677     QValueVector<QRgb> colorTable;
    00678 #else
    00679     QVector<QRgb> colorTable;
    00680 #endif
    00681     if ( colorMap.format() == QwtColorMap::Indexed )
    00682         colorTable = colorMap.colorTable(interval);
    00683
    00684     QColor c;
    00685
    00686     const QRect devRect = d_metricsMap.layoutToDevice(rect);
    00687
    00688     if ( orientation == Qt::Horizontal )
    00689     {
    00690         QwtScaleMap sMap = scaleMap;
    00691         sMap.setPaintInterval(devRect.left(), devRect.right());
    00692
    00693         for ( int x = devRect.left(); x <= devRect.right(); x++ )
    00694         {
    00695             const double value = sMap.invTransform(x);
    00696
    00697             if ( colorMap.format() == QwtColorMap::RGB )
    00698                 c.setRgb(colorMap.rgb(interval, value));
    00699             else
    00700                 c = colorTable[colorMap.colorIndex(interval, value)];
    00701
    00702             painter->setBrush(QBrush(c));
    00703
    00704             const QRect r(x, devRect.top(), 1, devRect.height());
    00705             QwtPainter::drawRect(painter, r);
    00706             painter->setPen(c);
    00707             painter->drawLine(x, devRect.top(), x, devRect.bottom() - 1);
    00708         }
    00709     }
    00710     else // Vertical
    00711     {
    00712         QwtScaleMap sMap = scaleMap;
    00713         sMap.setPaintInterval(devRect.bottom(), devRect.top());
    00714
    00715         for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
    00716         {
    00717             const double value = sMap.invTransform(y);
    00718
    00719             if ( colorMap.format() == QwtColorMap::RGB )
    00720                 c.setRgb(colorMap.rgb(interval, value));
    00721             else
    00722                 c = colorTable[colorMap.colorIndex(interval, value)];
    00723
    00724             painter->setPen(c);
    00725             painter->drawLine(devRect.left(), y, devRect.right() - 1, y);
    00726         }
    00727     }
    00728     painter->restore();
    00729 }