123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 |
- /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
- * Qwt Widget Library
- * Copyright (C) 1997 Josef Wilgen
- * Copyright (C) 2002 Uwe Rathmann
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the Qwt License, Version 1.0
- *****************************************************************************/
- #include "qwt_painter.h"
- #include "qwt_math.h"
- #include "qwt_clipper.h"
- #include "qwt_color_map.h"
- #include "qwt_scale_map.h"
- #include <qwindowdefs.h>
- #include <qwidget.h>
- #include <qrect.h>
- #include <qpainter.h>
- #include <qpalette.h>
- #include <qpaintdevice.h>
- #include <qpixmap.h>
- #include <qstyle.h>
- #include <qtextdocument.h>
- #include <qabstracttextdocumentlayout.h>
- #include <qstyleoption.h>
- #include <qpaintengine.h>
- #include <qapplication.h>
- #include <qdesktopwidget.h>
- bool QwtPainter::d_polylineSplitting = true;
- bool QwtPainter::d_roundingAlignment = true;
- static inline bool isClippingNeeded( const QPainter *painter, QRectF &clipRect )
- {
- bool doClipping = false;
- const QPaintEngine *pe = painter->paintEngine();
- if ( pe && pe->type() == QPaintEngine::SVG )
- {
- // The SVG paint engine ignores any clipping,
- if ( painter->hasClipping() )
- {
- doClipping = true;
- clipRect = painter->clipRegion().boundingRect();
- }
- }
- return doClipping;
- }
- static inline void drawPolyline( QPainter *painter,
- const QPointF *points, int pointCount, bool polylineSplitting )
- {
- bool doSplit = false;
- if ( polylineSplitting )
- {
- const QPaintEngine *pe = painter->paintEngine();
- if ( pe && pe->type() == QPaintEngine::Raster )
- {
- /*
- The raster paint engine seems to use some algo with O(n*n).
- ( Qt 4.3 is better than Qt 4.2, but remains unacceptable)
- To work around this problem, we have to split the polygon into
- smaller pieces.
- */
- doSplit = true;
- }
- }
- if ( doSplit )
- {
- const int splitSize = 20;
- for ( int i = 0; i < pointCount; i += splitSize )
- {
- const int n = qMin( splitSize + 1, pointCount - i );
- painter->drawPolyline( points + i, n );
- }
- }
- else
- painter->drawPolyline( points, pointCount );
- }
- static inline void unscaleFont( QPainter *painter )
- {
- if ( painter->font().pixelSize() >= 0 )
- return;
- static QSize screenResolution;
- if ( !screenResolution.isValid() )
- {
- QDesktopWidget *desktop = QApplication::desktop();
- if ( desktop )
- {
- screenResolution.setWidth( desktop->logicalDpiX() );
- screenResolution.setHeight( desktop->logicalDpiY() );
- }
- }
- const QPaintDevice *pd = painter->device();
- if ( pd->logicalDpiX() != screenResolution.width() ||
- pd->logicalDpiY() != screenResolution.height() )
- {
- QFont pixelFont( painter->font(), QApplication::desktop() );
- pixelFont.setPixelSize( QFontInfo( pixelFont ).pixelSize() );
- painter->setFont( pixelFont );
- }
- }
- /*!
- Check if the painter is using a paint engine, that aligns
- coordinates to integers. Today these are all paint engines
- beside QPaintEngine::Pdf and QPaintEngine::SVG.
- \param painter Painter
- \return true, when the paint engine is aligning
- \sa setRoundingAlignment()
- */
- bool QwtPainter::isAligning( QPainter *painter )
- {
- if ( painter && painter->isActive() )
- {
- switch ( painter->paintEngine()->type() )
- {
- case QPaintEngine::Pdf:
- case QPaintEngine::SVG:
- return false;
- default:;
- }
- }
- return true;
- }
- /*!
- Enable whether coordinates should be rounded, before they are painted
- to a paint engine that floors to integer values. For other paint engines
- this ( Pdf, SVG ), this flag has no effect.
- QwtPainter stores this flag only, the rounding itsself is done in
- the painting code ( f.e the plot items ).
- The default setting is true.
- \sa roundingAlignment(), isAligning()
- */
- void QwtPainter::setRoundingAlignment( bool enable )
- {
- d_roundingAlignment = enable;
- }
- /*!
- \brief En/Disable line splitting for the raster paint engine
- The raster paint engine paints polylines of many points
- much faster when they are splitted in smaller chunks.
- \sa polylineSplitting()
- */
- void QwtPainter::setPolylineSplitting( bool enable )
- {
- d_polylineSplitting = enable;
- }
- //! Wrapper for QPainter::drawRect()
- void QwtPainter::drawRect( QPainter *painter, double x, double y, double w, double h )
- {
- drawRect( painter, QRectF( x, y, w, h ) );
- }
- //! Wrapper for QPainter::drawRect()
- void QwtPainter::drawRect( QPainter *painter, const QRectF &rect )
- {
- const QRectF r = rect;
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- if ( deviceClipping )
- {
- if ( !clipRect.intersects( r ) )
- return;
- if ( !clipRect.contains( r ) )
- {
- fillRect( painter, r & clipRect, painter->brush() );
- painter->save();
- painter->setBrush( Qt::NoBrush );
- drawPolyline( painter, QPolygonF( r ) );
- painter->restore();
- return;
- }
- }
- painter->drawRect( r );
- }
- //! Wrapper for QPainter::fillRect()
- void QwtPainter::fillRect( QPainter *painter,
- const QRectF &rect, const QBrush &brush )
- {
- if ( !rect.isValid() )
- return;
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- /*
- Performance of Qt4 is horrible for non trivial brushs. Without
- clipping expect minutes or hours for repainting large rects
- (might result from zooming)
- */
- if ( deviceClipping )
- clipRect &= painter->window();
- else
- clipRect = painter->window();
- if ( painter->hasClipping() )
- clipRect &= painter->clipRegion().boundingRect();
- QRectF r = rect;
- if ( deviceClipping )
- r = r.intersect( clipRect );
- if ( r.isValid() )
- painter->fillRect( r, brush );
- }
- //! Wrapper for QPainter::drawPie()
- void QwtPainter::drawPie( QPainter *painter, const QRectF &rect,
- int a, int alen )
- {
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- if ( deviceClipping && !clipRect.contains( rect ) )
- return;
- painter->drawPie( rect, a, alen );
- }
- //! Wrapper for QPainter::drawEllipse()
- void QwtPainter::drawEllipse( QPainter *painter, const QRectF &rect )
- {
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- if ( deviceClipping && !clipRect.contains( rect ) )
- return;
- painter->drawEllipse( rect );
- }
- //! Wrapper for QPainter::drawText()
- void QwtPainter::drawText( QPainter *painter, double x, double y,
- const QString &text )
- {
- drawText( painter, QPointF( x, y ), text );
- }
- //! Wrapper for QPainter::drawText()
- void QwtPainter::drawText( QPainter *painter, const QPointF &pos,
- const QString &text )
- {
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- if ( deviceClipping && !clipRect.contains( pos ) )
- return;
- painter->save();
- unscaleFont( painter );
- painter->drawText( pos, text );
- painter->restore();
- }
- //! Wrapper for QPainter::drawText()
- void QwtPainter::drawText( QPainter *painter,
- double x, double y, double w, double h,
- int flags, const QString &text )
- {
- drawText( painter, QRectF( x, y, w, h ), flags, text );
- }
- //! Wrapper for QPainter::drawText()
- void QwtPainter::drawText( QPainter *painter, const QRectF &rect,
- int flags, const QString &text )
- {
- painter->save();
- unscaleFont( painter );
- painter->drawText( rect, flags, text );
- painter->restore();
- }
- #ifndef QT_NO_RICHTEXT
- /*!
- Draw a text document into a rectangle
- \param painter Painter
- \param rect Traget rectangle
- \param flags Alignments/Text flags, see QPainter::drawText()
- \param text Text document
- */
- void QwtPainter::drawSimpleRichText( QPainter *painter, const QRectF &rect,
- int flags, const QTextDocument &text )
- {
- QTextDocument *txt = text.clone();
- painter->save();
- painter->setFont( txt->defaultFont() );
- unscaleFont( painter );
- txt->setDefaultFont( painter->font() );
- txt->setPageSize( QSizeF( rect.width(), QWIDGETSIZE_MAX ) );
- QAbstractTextDocumentLayout* layout = txt->documentLayout();
- const double height = layout->documentSize().height();
- double y = rect.y();
- if ( flags & Qt::AlignBottom )
- y += ( rect.height() - height );
- else if ( flags & Qt::AlignVCenter )
- y += ( rect.height() - height ) / 2;
- QAbstractTextDocumentLayout::PaintContext context;
- context.palette.setColor( QPalette::Text, painter->pen().color() );
- painter->translate( rect.x(), y );
- layout->draw( painter, context );
- painter->restore();
- delete txt;
- }
- #endif // !QT_NO_RICHTEXT
- //! Wrapper for QPainter::drawLine()
- void QwtPainter::drawLine( QPainter *painter,
- const QPointF &p1, const QPointF &p2 )
- {
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- if ( deviceClipping &&
- !( clipRect.contains( p1 ) && clipRect.contains( p2 ) ) )
- {
- QPolygonF polygon;
- polygon += p1;
- polygon += p2;
- drawPolyline( painter, polygon );
- return;
- }
- painter->drawLine( p1, p2 );
- }
- //! Wrapper for QPainter::drawPolygon()
- void QwtPainter::drawPolygon( QPainter *painter, const QPolygonF &polygon )
- {
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- QPolygonF cpa = polygon;
- if ( deviceClipping )
- cpa = QwtClipper::clipPolygonF( clipRect, polygon );
- painter->drawPolygon( cpa );
- }
- //! Wrapper for QPainter::drawPolyline()
- void QwtPainter::drawPolyline( QPainter *painter, const QPolygonF &polygon )
- {
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- QPolygonF cpa = polygon;
- if ( deviceClipping )
- cpa = QwtClipper::clipPolygonF( clipRect, cpa );
- ::drawPolyline( painter,
- cpa.constData(), cpa.size(), d_polylineSplitting );
- }
- //! Wrapper for QPainter::drawPolyline()
- void QwtPainter::drawPolyline( QPainter *painter,
- const QPointF *points, int pointCount )
- {
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- if ( deviceClipping )
- {
- QPolygonF polygon( pointCount );
- qMemCopy( polygon.data(), points, pointCount * sizeof( QPointF ) );
- polygon = QwtClipper::clipPolygonF( clipRect, polygon );
- ::drawPolyline( painter,
- polygon.constData(), polygon.size(), d_polylineSplitting );
- }
- else
- ::drawPolyline( painter, points, pointCount, d_polylineSplitting );
- }
- //! Wrapper for QPainter::drawPoint()
- void QwtPainter::drawPoint( QPainter *painter, const QPointF &pos )
- {
- QRectF clipRect;
- const bool deviceClipping = isClippingNeeded( painter, clipRect );
- if ( deviceClipping && !clipRect.contains( pos ) )
- return;
- painter->drawPoint( pos );
- }
- //! Wrapper for QPainter::drawImage()
- void QwtPainter::drawImage( QPainter *painter,
- const QRectF &rect, const QImage &image )
- {
- const QRect alignedRect = rect.toAlignedRect();
- if ( alignedRect != rect )
- {
- const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
- painter->save();
- painter->setClipRect( clipRect, Qt::IntersectClip );
- painter->drawImage( alignedRect, image );
- painter->restore();
- }
- else
- {
- painter->drawImage( alignedRect, image );
- }
- }
- //! Wrapper for QPainter::drawPixmap()
- void QwtPainter::drawPixmap( QPainter *painter,
- const QRectF &rect, const QPixmap &pixmap )
- {
- const QRect alignedRect = rect.toAlignedRect();
- if ( alignedRect != rect )
- {
- const QRectF clipRect = rect.adjusted( 0.0, 0.0, -1.0, -1.0 );
- painter->save();
- painter->setClipRect( clipRect, Qt::IntersectClip );
- painter->drawPixmap( alignedRect, pixmap );
- painter->restore();
- }
- else
- {
- painter->drawPixmap( alignedRect, pixmap );
- }
- }
- /*!
- Draw a arc with a linear gradient
- \note This method needs to be replaced by using QGradient
- */
- void QwtPainter::drawColoredArc( QPainter *painter, const QRect &rect,
- int peak, int arc, int interval, const QColor &c1, const QColor &c2 )
- {
- int h1, s1, v1;
- int h2, s2, v2;
- c1.getHsv( &h1, &s1, &v1 );
- c2.getHsv( &h2, &s2, &v2 );
- arc /= 2;
- for ( int angle = -arc; angle < arc; angle += interval )
- {
- double ratio;
- if ( angle >= 0 )
- ratio = 1.0 - angle / double( arc );
- else
- ratio = 1.0 + angle / double( arc );
- QColor c;
- c.setHsv( h1 + qRound( ratio * ( h2 - h1 ) ),
- s1 + qRound( ratio * ( s2 - s1 ) ),
- v1 + qRound( ratio * ( v2 - v1 ) ) );
- painter->setPen( QPen( c, painter->pen().width() ) );
- painter->drawArc( rect, ( peak + angle ) * 16, interval * 16 );
- }
- }
- //! Draw a focus rectangle on a widget using its style.
- void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget )
- {
- drawFocusRect( painter, widget, widget->rect() );
- }
- //! Draw a focus rectangle on a widget using its style.
- void QwtPainter::drawFocusRect( QPainter *painter, QWidget *widget,
- const QRect &rect )
- {
- QStyleOptionFocusRect opt;
- opt.init( widget );
- opt.rect = rect;
- opt.state |= QStyle::State_HasFocus;
- widget->style()->drawPrimitive( QStyle::PE_FrameFocusRect,
- &opt, painter, widget );
- }
- //! Draw a round frame
- void QwtPainter::drawRoundFrame( QPainter *painter, const QRect &rect,
- int width, const QPalette &palette, bool sunken )
- {
- QColor c0 = palette.color( QPalette::Mid );
- QColor c1, c2;
- if ( sunken )
- {
- c1 = palette.color( QPalette::Dark );
- c2 = palette.color( QPalette::Light );
- }
- else
- {
- c1 = palette.color( QPalette::Light );
- c2 = palette.color( QPalette::Dark );
- }
- painter->setPen( QPen( c0, width ) );
- painter->drawArc( rect, 0, 360 * 16 ); // full
- const int peak = 150;
- const int interval = 2;
- if ( c0 != c1 )
- drawColoredArc( painter, rect, peak, 160, interval, c0, c1 );
- if ( c0 != c2 )
- drawColoredArc( painter, rect, peak + 180, 120, interval, c0, c2 );
- }
- /*!
- Draw a color bar into a rectangle
- \param painter Painter
- \param colorMap Color map
- \param interval Value range
- \param scaleMap Scale map
- \param orientation Orientation
- \param rect Traget rectangle
- */
- void QwtPainter::drawColorBar( QPainter *painter,
- const QwtColorMap &colorMap, const QwtInterval &interval,
- const QwtScaleMap &scaleMap, Qt::Orientation orientation,
- const QRectF &rect )
- {
- QVector<QRgb> colorTable;
- if ( colorMap.format() == QwtColorMap::Indexed )
- colorTable = colorMap.colorTable( interval );
- QColor c;
- const QRect devRect = rect.toAlignedRect();
- /*
- We paint to a pixmap first to have something scalable for printing
- ( f.e. in a Pdf document )
- */
- QPixmap pixmap( devRect.size() );
- QPainter pmPainter( &pixmap );
- pmPainter.translate( -devRect.x(), -devRect.y() );
- if ( orientation == Qt::Horizontal )
- {
- QwtScaleMap sMap = scaleMap;
- sMap.setPaintInterval( rect.left(), rect.right() );
- for ( int x = devRect.left(); x <= devRect.right(); x++ )
- {
- const double value = sMap.invTransform( x );
- if ( colorMap.format() == QwtColorMap::RGB )
- c.setRgb( colorMap.rgb( interval, value ) );
- else
- c = colorTable[colorMap.colorIndex( interval, value )];
- pmPainter.setPen( c );
- pmPainter.drawLine( x, devRect.top(), x, devRect.bottom() );
- }
- }
- else // Vertical
- {
- QwtScaleMap sMap = scaleMap;
- sMap.setPaintInterval( rect.bottom(), rect.top() );
- for ( int y = devRect.top(); y <= devRect.bottom(); y++ )
- {
- const double value = sMap.invTransform( y );
- if ( colorMap.format() == QwtColorMap::RGB )
- c.setRgb( colorMap.rgb( interval, value ) );
- else
- c = colorTable[colorMap.colorIndex( interval, value )];
- pmPainter.setPen( c );
- pmPainter.drawLine( devRect.left(), y, devRect.right(), y );
- }
- }
- pmPainter.end();
- drawPixmap( painter, rect, pixmap );
- }
|