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 }

Generated on Thu May 1 15:44:08 2008 for Qwt User's Guide by  doxygen 1.5.0