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 }

Generated on Mon Jun 11 07:41:38 2007 for Qwt User's Guide by  doxygen 1.4.6